# options
options(stringsAsFactors = F)
knitr::opts_chunk$set(echo = TRUE, warning = FALSE, message = FALSE)
knitr::opts_knit$set(progress = FALSE)


We feed log transformed values, using log1p() base R function, for accessibility at 15,000 regions in the genome with the most variance in chromatin accessibility, all transcripts and all proteins with abundance measurements into MOFA+ for model generation.



Figure 5A: MOFA factors overview

# create the MOFA object
all_df_shared_MOFAobject <- create_mofa(all_df_shared)

# Load in model results generated using _src/MOFA_train_model_CPU.r
all_df_shared_model <- load_model(here("../pQTL_website/_data/all_df_shared_30factors_2022-04-11.hdf5"), remove_inactive_factors = F)
# Warning message:
# In .quality_control(object, verbose = verbose) :
#   Factor(s) 3 are strongly correlated with the total number of expressed features for at least one of your omics. Such factors appear when there are differences in the total 'levels' between your samples, *sometimes* because of poor normalisation in the preprocessing steps.

# Removing Factor 3 that shows high correlation to the total # of expressed features.
all_df_shared_model <- subset_factors( all_df_shared_model, factors = c(1:2, 4:30))

# Removing Factors that don't explain at least 1% of variation in a data set. 
all_df_shared_factor_var <- all_df_shared_model@cache$variance_explained$r2_per_factor %>%
  as.data.frame() %>%
  rownames_to_column() %>%
  filter(single_group.Chromatin > 1 |
           single_group.Protein > 1 |
           single_group.Transcript > 1) %>%
  column_to_rownames()
all_df_shared_model_filtered <- subset_factors(all_df_shared_model,
                                        factors = as.numeric(gsub("Factor","",rownames(all_df_shared_factor_var))))

# updating the sexes in the metadata to use Female/Male instead of 0s and 1s.
merged_metadata <- merged.covar2 %>%
  as.data.frame() %>%
  rownames_to_column() %>%
  select(-lifr) %>%
  rename("sample"="rowname")
shared_samples_metadata <- all_df_shared_model_filtered@samples_metadata %>%
  left_join(.,merged_metadata) %>%
  mutate(sex = ifelse(sex ==0,"Female", "Male"))
samples_metadata(all_df_shared_model_filtered) <- shared_samples_metadata


# Get % variance explained for each factor
all_df_shared_var_explained <- (calculate_variance_explained(all_df_shared_model_filtered))

# Get Factor weights
all_df_shared_factors <- get_factors(all_df_shared_model_filtered,
  factors = "all",
  as.data.frame = T
)
# Add sample details and convert to matrix with Factors in rows and samples in columns.
all_df_shared_factors_mat <- all_df_shared_factors %>%
  pivot_wider(id_cols = "sample", names_from = "factor", values_from = "value") %>%
  left_join(., select(threeway.shared.samples, top_muga, sampleid), by =c("sample"="top_muga")) %>%
  column_to_rownames("sample") %>%
  select(-sampleid) %>%
  as.matrix()

# Get feature weights
all_df_shared_weights <- get_weights(all_df_shared_model_filtered, as.data.frame = TRUE, scale = TRUE)

# Correlate Factors with covariates and get R values
correlate_factors_with_covariates(all_df_shared_model_filtered,
  covariates = c("sex","lifr_geno"),
  plot="r",
  return_data = TRUE) %>%
  as_tibble( rownames = "Factor") %>%
  pivot_longer( cols = c("sex","lifr_geno"),
                names_to = "covariate",
                values_to = "pearson_r") -> corr_to_cov_r

# Correlate Factors with covariates and get p-values
correlate_factors_with_covariates(all_df_shared_model_filtered,
  covariates = c("sex","lifr_geno"),
  plot="log_pval",
  return_data = TRUE) %>%
  as_tibble( rownames = "Factor") %>%
  pivot_longer( cols = c("sex","lifr_geno"),
                names_to = "covariate",
                values_to = "log_pval") -> corr_to_cov_logpval

# merge R & p-values into a single data frame
corr_to_cov_r %>%
  full_join( corr_to_cov_logpval) -> all_df_shared_corr_covar


# get atac-seq peak drivers + run overrepresentation analysis using LOLA

# these are all the ATAC-seq peaks that are fed into MOFA that we will use as the custom background in overrepresentation analysis
background_atac_peaks <-  all_df_shared %>%
  filter(view == c("Chromatin"))  %>%
  rename( peak_id = feature) %>%
  select( -view,-value, -sample) %>%
  distinct() %>%
  separate( peak_id, into = c("Chr", "Start","End"), remove = FALSE) %>%
  mutate( Chr = gsub("peak","chr",Chr)) %>%
  makeGRangesFromDataFrame(.,
                               keep.extra.columns = F,
                               seqnames.field = c("Chr"),
                               start.field = "Start",
                               end.field = "End")

get_tf_ora <- function( factor_df, factor_num, bg_peaks){
  
  # get top ATAC-seq peak drivers
  top_atac_drivers <- factor_df %>%
    slice( which( factor_df$value %in%
        boxplot.stats( filter(factor_df, factor == factor_num & view =="Chromatin")$value)$out )
    )
  
  # convert the top ATAC-seq peak drivers to a compatible object for LOLA
  atac_peaks_for_lola <- top_atac_drivers %>%
    rename( peak_id = feature) %>%
    separate( peak_id, into = c("Chr", "Start","End"), remove = FALSE) %>%
    mutate( Chr = gsub("peak","chr",Chr)) %>%
    makeGRangesFromDataFrame(.,
                               keep.extra.columns = F,
                               seqnames.field = c("Chr"),
                               start.field = "Start",
                               end.field = "End")
  # run LOLA with custom background
  lola_results <- runLOLA(atac_peaks_for_lola,
                          bg_peaks,
                          regionDB,
                          cores=1)
  # add q-value for filtering later
  lola_results$qValue <- (qvalue( 10^(-lola_results$pValueLog )))$qvalues

  return(lola_results)
  
}

# get_tf_ora( all_df_shared_weights, "Factor1", background_atac_peaks)
# I ran the function above for all factors and saved the output
# below is the filtered results for qvalue <0.05 and cell type as ESCs
load( here("../pQTL_website/_data/","MOFA_Factor_LOLA_results.RData")) # lola_combined


Figure5A_data_var_exp <- all_df_shared_var_explained$r2_per_factor$single_group %>%
  as_tibble(rownames = "Factor") %>%
  mutate(factor_num = as.numeric(gsub( "Factor" ,"",Factor)) )%>%
  arrange((factor_num)) %>%
  pivot_longer(c("Chromatin","Protein","Transcript")) %>%
  mutate( Factor = factor(Factor, levels = unique(Factor))) %>%
  mutate( name = factor( name, levels = c("Chromatin","Transcript","Protein"))) %>%
  select( `Data set` = name, Factor, `% Variation Explained` = value)

Figure5A_data_cor_to_covar <- all_df_shared_corr_covar %>%
  mutate( Factor = factor(Factor, levels = unique(Factor))) %>%
  mutate( covariate = ifelse( covariate =="sex", "Sex", "LIFR genotype")) %>%
  mutate( covariate = factor( covariate, levels = c("Sex", "LIFR genotype"))) %>%
  select(Factor, covariate, Correlation =pearson_r)

Figure5A_data_tf_ora <- lola_combined %>% 
  filter( antibody %in% c("Nanog","Sox2","Pou5f1")) %>% 
  filter( description %in% c("ES cells expressing control shRNA targeting GFP",
                             "V6.5 (C57BL/6-129) murine ES cells were grown under typical ES conditions on irr",
                             "N/A")) %>% 
  filter( !description %in% c("ES cells expressing shRNA targeting Kdm4b",
                             "ES cells expressing shRNA targeting Kdm4c",
                             "ES cells cultured in FGF4; heparin and Activin A for 1 day after Oct3/4 deleted",
                             "ES cells differentiated in vitro for 2 days; Brn2 expression induced using Tet-O",
                             "ES cells cultured in FGF4; heparin and Activin A for 2 days after Oct3/4 deleted",
                             "ES cells cultured in FGF4; heparin and Activin A for 3 days after Oct3/4 deleted",
                             "ES cells differentiated in vitro for 2 days")) %>% 
  full_join( tibble(
                factor = c( paste0("Factor ", seq(1:23))),
                factor_num = seq(1:23)) 
              ) %>% 
  group_by(factor, antibody, factor_num) %>% 
  dplyr::summarize( mean_odds = mean(oddsRatio, na.rm= T)) %>% 
  ungroup() %>% 
  arrange(factor_num) %>% 
  mutate( Factor = factor(factor, levels = unique(factor))) %>%
  select(Factor, TF = antibody, `Odds ratio` = mean_odds)
# % variation explained heatmap.
Figure5A_data_var_exp %>% 
  ggplot()+
  aes( 
       y = Factor,
       x = `Data set`,
       fill = `% Variation Explained`)+
  geom_tile()+
  xlab("")+
  ylab("")+
  theme_pubclean( base_size = 18)+
  scale_fill_gradient2( limits = c(0, 10))+
  #scale_size_area()+
  labs(fill = "% Variation Explained", col = "")+
  scale_x_discrete( expand = expansion(mult = 0))+
  theme(
        legend.position = "top", 
        legend.title = element_text(size = 18, vjust = 0.9),
        legend.text  = element_text(angle=45, size =12, vjust = 0.5),
        )-> all_df_shared_var_plot

# correlation to covariates.
Figure5A_data_cor_to_covar %>%
  filter(covariate == "Sex") %>% 
  ggplot()+
  aes(
    x = covariate,
    y = Factor,
    fill = abs(Correlation)
  )+
  geom_tile()+
  xlab("")+
  ylab("")+
  theme_pubclean(base_size = 18)+
  scale_fill_gradient(low = "white",high = "dark red", limits = c(0,1))+
  theme(legend.position = "top",
        axis.ticks.y = element_blank(),
        legend.text  = element_text(angle=45, size =12, vjust = 0.5),
        legend.title = element_text(size = 18, vjust = 0.9),
        )+
  scale_y_discrete( labels = NULL)+
  scale_x_discrete( expand = expansion(mult = 0))+
  labs( fill = "Correlation") -> all_df_shared_covar_plot_sex

Figure5A_data_cor_to_covar %>%
  filter(covariate != "Sex") %>% 
  ggplot()+
  aes(
    x = covariate,
    y = Factor,
    fill = abs(Correlation)
  )+
  geom_tile()+
  xlab("")+
  ylab("")+
  theme_pubclean(base_size = 18)+
  scale_fill_gradient(low = "white",high = "deeppink4", limits = c(0,1))+
  theme(legend.position = "top",
        axis.ticks.y = element_blank(),
        legend.text  = element_text(angle=45, size =12, vjust = 0.5),
        legend.title = element_text(size =12)
        )+
  scale_y_discrete( labels = NULL)+
  scale_x_discrete( expand = expansion(mult = 0))+
  labs( fill = "") -> all_df_shared_covar_plot_lifr

# Odds ratio for TF binding overrepresentation for NANOG, OCT4, SOX2 in ATACseq peak drivers
Figure5A_data_tf_ora %>% 
  mutate( mean_odds_capped = ifelse( `Odds ratio` >10, 10, `Odds ratio`)) %>% 
  ggplot()+
  aes(
    x = TF,
    y = Factor,
    fill = mean_odds_capped
  )+
  geom_tile()+
  xlab("")+
  ylab("")+
  theme_pubclean(base_size = 18)+
  scale_fill_gradient(low = "light gray",high = "#ffa08aff", limits = c(0,10)) +
  theme(legend.position = "top",
        legend.text  = element_text(angle=45, size =12, vjust = 0.5),
        legend.title = element_text(size = 18, vjust = 0.9),
        axis.text.x =  element_text(angle = 0),
         axis.ticks.y = element_blank(),
        )+
  scale_y_discrete( labels = NULL)+
  scale_x_discrete( expand = expansion(mult = 0))+
  labs( fill = "Odds ratio") -> lola_sum_figure

figure5a <- ggarrange( all_df_shared_var_plot,
                       all_df_shared_covar_plot_sex,
                       all_df_shared_covar_plot_lifr,
                       lola_sum_figure,
                       widths = c(0.45,0.15,0.15,0.2), nrow = 1,
                       labels = "A",
                       font.label = list( size = 28))


figure5a
MOFA yielded 23 latent factors that capture variation in one or more layers of genomic data. For each factor, percent of variation explained in chromatin accessibility, transcript abundance, and protein abundance is displayed as a heatmap, as is the correlation of each factor to experimental covariates including sex and genotype at the Lifr locus. Heatmap on the right indicates overrepresentation of pluripotency regulator binding sites (NANOG, OCT4 (Pou5f1) and SOX2) among the top chromatin drivers of each factor.

MOFA yielded 23 latent factors that capture variation in one or more layers of genomic data. For each factor, percent of variation explained in chromatin accessibility, transcript abundance, and protein abundance is displayed as a heatmap, as is the correlation of each factor to experimental covariates including sex and genotype at the Lifr locus. Heatmap on the right indicates overrepresentation of pluripotency regulator binding sites (NANOG, OCT4 (Pou5f1) and SOX2) among the top chromatin drivers of each factor.


Figure5A_data_var_exp %>% 
  mutate_if( is.numeric, round, 2) %>% 
  create_dt()


Figure5A_data_cor_to_covar %>% 
  mutate_if( is.numeric, round, 2) %>% 
  create_dt()


Figure5A_data_tf_ora %>% 
  mutate_if( is.numeric, round, 2) %>% 
  create_dt()


Table S7: MOFA factor values for samples and lists of molecular features with their weights for each of 23 MOFA factors.

MOFA factor values (columns) for each of the 163 samples (rows) are listed in the first tab. Weights for each of the 23 MOFA factors (columns) are listed for every protein, transcript, and chromatin peak (rows), with tabs corresponding to the three data types.Feature annotations include Ensembl protein identifier and gene symbol for proteins, Ensembl gene identifier and gene symbol for transcripts, and chromatin peak identifier and proximal gene annotation from R/ChIPseeker for ATAC-seq peaks.


Figure S5A: Drivers of MOFA factors.

all_df_shared_weights %>% 
  filter( abs(value) > 0.01) %>%
  #group_by(view) %>% summarise( value = median(value))
  group_by(factor) %>% 
  count( view, .drop = FALSE) %>%
  ggplot()+
  aes(
    x = view,
    y = factor,
    fill = n
    )+
  geom_tile()+
  xlab("")+
  ylab("")+
  theme_pubclean( base_size = 18)+
  #scale_color_viridis_c(option = "C")+
  scale_fill_gradient2(limits = c(0, 15000))+
  labs(fill = "# of features", col = "")+
  scale_x_discrete( expand = expansion(mult = 0))+
  theme(
        legend.position = "top", #c(0.75,0.95),
        #axis.line.y = element_blank(),
        legend.title = element_text(size = 18, vjust = 0.9),
        legend.text  = element_text(angle=45, size =12, vjust = 0.5),
        ) 
Figure S5. Heatmaps showing the number of features in each data set with abs(weight) > 0.01 for 23 MOFA Factors.

Figure S5. Heatmaps showing the number of features in each data set with abs(weight) > 0.01 for 23 MOFA Factors.



Figure 5B: QTL mapping with MOFA Factors

# load from file
knitr::include_graphics(here("Figure5B.png"))
Figure 5B: Above shows a depiction of QTL mapping with MOFA factors to identify the genetic modifiers driving variation across three molecular layers. Below is a table of QTL peaks that map above the genome-wide significance threshold calculated individually for each factor. Loci that were previously observed as molecular QTL hotspots are denoted in the “Type” column.

Figure 5B: Above shows a depiction of QTL mapping with MOFA factors to identify the genetic modifiers driving variation across three molecular layers. Below is a table of QTL peaks that map above the genome-wide significance threshold calculated individually for each factor. Loci that were previously observed as molecular QTL hotspots are denoted in the “Type” column.


# rankZ transform before mapping
all_df_shared_factors_mat_rankZ <- apply( all_df_shared_factors_mat, 2, rankZ)



# run qtl scans with sex as an additive covariate
all_df_scans <- scan1( genoprobs = shared.probs,
                       pheno = all_df_shared_factors_mat_rankZ,
                       kinship = shared.kinship,
                       addcovar = shared.covar)

# get peaks above lod 5
all_df_shared_peaks <- find_peaks( all_df_scans, map = gmap, threshold = 5)
# interpolate physical location of peaks 
all_df_shared_peaks <- all_df_shared_peaks %>%
  mutate(phenotype=lodcolumn) %>%
  mutate( peak_chr = chr,
          peak_cM = pos,
          factor_num = as.numeric(str_sub(lodcolumn, 7)) )%>%
  interp_bp(.) #add bp location for peaks

# add markers before & after the QTL peaks for getting effects later
# Get the bounding markers for each QTL peak
# i.e. markers on the 69k grid that are up- and downstream of the peak
query <- all_df_shared_peaks %>% 
  dplyr::select(peak_chr, interp_bp_peak) %>%
  dplyr::rename(chrom=peak_chr, start=interp_bp_peak) %>% mutate(end=start) %>%
  GenomicRanges::GRanges()
subject <- select(map_dat2, chrom, pos_bp) %>% 
  dplyr::rename(start=pos_bp) %>%
  mutate(end=start) %>% 
  GenomicRanges::GRanges()   # length 69,005

all_df_shared_peaks$before <- map_dat2$marker[follow(query, subject)]
all_df_shared_peaks$after <- map_dat2$marker[precede(query, subject)]

# loading the permutation results for significance thresholds
load(here("../pQTL_website/_data/MOFA_all_df_thres_rankZ.RData"))


# get the list of significant MOFA QTL
summary(all_df_shared_thres) %>%
  as_tibble( rownames = "Significance level") %>%
  pivot_longer(names_to = "Factor", values_to="thres", cols = 2:24) %>%
  left_join( all_df_shared_peaks, by = c("Factor"= "phenotype")) %>%
  mutate( phenotype = Factor) %>%
  mutate( lod = round(lod,1), thres = round(thres, 1))  %>%
  filter( lod >= thres  ) %>%
  filter( !(chr =="X" & lod <= thres+5) ) -> all_df_shared_peaks_sig

all_df_shared_peaks_sig %>% 
  mutate( peak_pos_Mbp = round((interp_bp_peak/1e06),2),
          lod = round(lod,1),
          factor_num = paste("Factor", factor_num),
          qtl_loc = paste0(chr, ": ",peak_pos_Mbp)) %>% 
  select( `MOFA Factor` = factor_num,
          `QTL Peak location (Chr: Mbp)` = qtl_loc,
          `LOD score` = lod) %>% 
  mutate(
    Type = case_when( 
      `MOFA Factor` %in% c("Factor 3","Factor 13")~"caQTL, eQTL",
      `MOFA Factor` == "Factor 4"~"eQTL",
      `MOFA Factor` %in% c("Factor 12","Factor 16")~"New",
      `MOFA Factor` == "Factor 14" & `QTL Peak location (Chr: Mbp)`=="16: 65.58"~"New",
      `MOFA Factor` == "Factor 14" & `QTL Peak location (Chr: Mbp)`=="4: 147.89"~"caQTL, eQTL",
      )
  ) %>% 
  create_dt()

Data shown in Figure 5B.



Figure 5C: MOFA Factor 3 QTL on chromosome 15

# get allele effects for MOFA Factor 3 on chr 15
factor3_chr15_qtl <- all_df_shared_peaks_sig %>%
  filter( factor_num ==3, chr == 15)

# run effects scan using qtl2::scan1blup & get the mean of allele effects between QTL borders (markers before & after)
factor3_chr15_qtl_effs_scan <- scan1blup(
  genoprobs =shared.probs[,15],
  pheno = all_df_shared_factors_mat_rankZ[,"Factor3", drop = FALSE],
  kinship = shared.kinship[[15]],
  addcovar = shared.covar)
factor3_chr15_qtl_effs <- colMeans(factor3_chr15_qtl_effs_scan[c(factor3_chr15_qtl$before, factor3_chr15_qtl$after), LETTERS[1:8]]) 

# # get lod scores + effects for all the proteins at the same locus
pQTL_at_chr15_peak <- c()
mofa_factor <- "Factor3"
mofa_peak_chr <- factor3_chr15_qtl$chr
mofa_markers <- c(factor3_chr15_qtl$before, factor3_chr15_qtl$after)
probs2_markers <- subset_probs( probs.esc_prot,
                                this_chrom =mofa_peak_chr,
                                this_markers = mofa_markers )

for( i in 1:nrow(all.prots)){
    probs2_markers <- subset_probs( probs.esc_prot,
                                    this_chrom = mofa_peak_chr,
                                    this_markers = mofa_markers )
    pQTL_eff <- scan1blup(genoprobs = probs2_markers,
                          pheno = exprZ.esc_prot[, all.prots$protein_id[i],drop=FALSE],
                          kinship_loco.esc_prot[[mofa_peak_chr]],
                          addcovar = covar.esc_prot
    )
    pQTL_mean_eff <- colMeans(pQTL_eff[,LETTERS[1:8]])
    names(pQTL_mean_eff) <- paste0(LETTERS[1:8],".esc_prot")

    pQTL_at_chr15_peak[[i]] <- c( protein_id = all.prots$protein_id[i],
                                  peak_chr = mofa_peak_chr,
                                  Factor = mofa_factor,
                                  pQTL_mean_eff)

  }

# pQTL
# convert to matrix
pQTL_at_chr15_peak_effs <- pQTL_at_chr15_peak %>% 
  do.call(rbind,.) %>% 
  as_tibble() %>% 
  mutate_at(., c(paste0(LETTERS[1:8],".esc_prot")), as.numeric )
pQTL_at_chr15_peak_eff_mat <- pQTL_at_chr15_peak_effs%>% 
  select(c(protein_id,paste0(LETTERS[1:8],".esc_prot")) ) %>% 
  column_to_rownames("protein_id") %>%
  as.matrix() %>% 
  t()
rownames(pQTL_at_chr15_peak_eff_mat) <- LETTERS[1:8]
factor3_chr15_qtl_effs_mat <- factor3_chr15_qtl_effs %>% 
  as.matrix()
# get correlations
factor3_pqtl_effs_corr <- cor(pQTL_at_chr15_peak_eff_mat, factor3_chr15_qtl_effs_mat)
factor3_pqtl_effs_wcorr <- factor3_pqtl_effs_corr %>% 
  as_tibble( rownames = "protein_id") %>% 
  rename( "pqtl_cor"= V1) 

# add pQTL lod for each protein
# get pQTL lods from scans + add ids
# load scans
load("/projects/munger-lab/projects/DO_mESC/proteomics/pqtl_mapping_SA/DO195_mESC_pQTL_scans_noPoly_v2.RData") # esc.prot.scans
factor3_pqtl_lods <- apply( esc.prot.scans[c(factor3_chr15_qtl$before, factor3_chr15_qtl$after),],2,max ) %>% 
  as_tibble( rownames = "protein_id") %>% 
  rename( pQTL_lod = value)
rm(esc.prot.scans)

# merge all into a data frame for plotting
Figure5C_data <- factor3_pqtl_effs_wcorr %>% 
  left_join( factor3_pqtl_lods) %>% 
  left_join( all_df_shared_weights %>% 
               filter( factor == "Factor3", feature %in% all.prots$protein_id) %>% 
               select( protein_id = feature, protein_weight = value)
  ) %>%
  # add protein details
  left_join( ., all.prots2 %>% 
               select( protein_id, gene_chr, midpoint, mgi_symbol)) %>% 
  # add mofa qtl details
  cbind( ., factor3_chr15_qtl %>% 
           select( factor3_qtl_pos =interp_bp_peak , factor3_qtl_peak_chr = peak_chr)
         ) %>% 
  filter(!is.na(pqtl_cor)) %>% 
  filter(  !(factor3_qtl_peak_chr == gene_chr &
              abs( midpoint - as.numeric(factor3_qtl_pos)) < 10e06 ) ) %>% 
  select(
    `Protein ID` = protein_id,
    `MGI symbol` = mgi_symbol,
    `Correlation between QTL effects` = pqtl_cor,
    `LOD score` = pQTL_lod,
    `Protein weight` = protein_weight
  )


Figure5C_data %>% 
  filter( `LOD score` < 7.5, 
          abs(`Protein weight`) >0.1 ) %>% 
  ggplot()+
  aes(
    x = `Protein weight`,
    y =  `LOD score`,
    col = `Correlation between QTL effects`,
    text = paste("Gene: ", `MGI symbol`) 
  )+
  geom_point(size = 3, alpha = 0.6)+
  theme_pubclean(base_size = 18)+
  scale_color_gradient2( limits = c(-1,1))+
  xlab("Protein weight")+
  ylab("LOD score")+
  labs( col = "Correlation between\nQTL effects") +
  xlim(-0.75,0.4)+
  ylim(0,15)+
  theme(legend.text = element_text(angle = 30), 
        legend.position = "top",
        legend.title = element_text(vjust = 1.3))+
  # add the signifincatn qtl in gray
  geom_point( data = filter(Figure5C_data, `LOD score` > 7.5, abs(`Protein weight`)>0.1),
              col = "gray", size =3, alpha = 0.6)-> Figure5C

#ggplotly(Figure5C,  width = 700, height = 400)
 
Figure5C 
Figure 5C: For all expressed proteins, the pQTL LOD score calculated at the Chr 15 QTL peak is plotted on the y-axis relative to the protein’s contribution (factor weight) to MOFA Factor 3 on the x-axis. Proteins with absolute factor weights less than 0.1 were filtered. Correlation between allele effects at the Chr 15 pQTL for individual proteins to allele effects of the Factor 3 QTL. Individual genes that mapped with a significant QTL (LOD > 7.5) are colored gray, and highlight that many proteins contribute substantially to Factor 3 and show high agreement in allele effects at the Chr 15 QTL (dark red and blue), despite not mapping individually with a significant QTL at that locus.

Figure 5C: For all expressed proteins, the pQTL LOD score calculated at the Chr 15 QTL peak is plotted on the y-axis relative to the protein’s contribution (factor weight) to MOFA Factor 3 on the x-axis. Proteins with absolute factor weights less than 0.1 were filtered. Correlation between allele effects at the Chr 15 pQTL for individual proteins to allele effects of the Factor 3 QTL. Individual genes that mapped with a significant QTL (LOD > 7.5) are colored gray, and highlight that many proteins contribute substantially to Factor 3 and show high agreement in allele effects at the Chr 15 QTL (dark red and blue), despite not mapping individually with a significant QTL at that locus.


Figure5C_data %>% 
  mutate_if( is.numeric, round, 2) %>% 
  create_dt()

Data plotted in Figure 5C.



Figure 5D: MOFA Factor 4 QTL on chromosome 10

# get allele effects for MOFA Factor 4 on chr 10
factor4_chr10_qtl <- all_df_shared_peaks_sig %>%
  filter( factor_num == 4, chr == 10)

# run effects scan using qtl2::scan1blup & get the mean of allele effects between QTL borders (markers before & after)
factor4_chr10_qtl_effs_scan <- scan1blup(
  genoprobs =shared.probs[,10],
  pheno = all_df_shared_factors_mat_rankZ[,"Factor4", drop = FALSE],
  kinship = shared.kinship[[10]],
  addcovar = shared.covar)
factor4_chr10_qtl_effs <- colMeans(factor4_chr10_qtl_effs_scan[c(factor4_chr10_qtl$before, factor4_chr10_qtl$after), LETTERS[1:8]])

# get lod scores + effects for all the proteins at the same locus
eQTL_at_chr10_peak <- c()
mofa_factor <- "Factor4"
mofa_peak_chr <- factor4_chr10_qtl$chr  
mofa_markers <- c(factor4_chr10_qtl$before, factor4_chr10_qtl$after)
probs2_markers <- subset_probs( probs.esc_rna, 
                                this_chrom =mofa_peak_chr, 
                                this_markers = mofa_markers )

for( i in 1:nrow(all.genes)){
    probs2_markers <- subset_probs( probs.esc_rna, 
                                    this_chrom = mofa_peak_chr, 
                                    this_markers = mofa_markers )
  
    eQTL_eff <- scan1blup(genoprobs = probs2_markers,
                          pheno = exprZ.esc_rna[, all.genes$ensembl_gene_id[i],drop=FALSE],
                          kinship_loco.esc_rna[[mofa_peak_chr]],
                          addcovar = covar.esc_rna
    )
    eQTL_mean_eff <- colMeans(eQTL_eff[,LETTERS[1:8]])
    names(eQTL_mean_eff) <- paste0(LETTERS[1:8],".esc_rna")
    
    eQTL_at_chr10_peak[[i]] <- c( ensembl_gene_id = all.genes$ensembl_gene_id[i],
                                  peak_chr = mofa_peak_chr,
                                  Factor = mofa_factor,
                                  eQTL_mean_eff)
    
  }

# eQTL
# convert to matrix
eQTL_at_chr10_peak_effs <- eQTL_at_chr10_peak %>% 
  do.call(rbind,.) %>% 
  as_tibble() %>% 
  mutate_at(., c(paste0(LETTERS[1:8],".esc_rna")), as.numeric )
eQTL_at_chr10_peak_effs_mat <- eQTL_at_chr10_peak_effs%>% 
  select(c(ensembl_gene_id,paste0(LETTERS[1:8],".esc_rna")) ) %>% 
  column_to_rownames("ensembl_gene_id") %>%
  as.matrix() %>% 
  t()
rownames(eQTL_at_chr10_peak_effs_mat) <- LETTERS[1:8]
factor4_chr10_qtl_effs_mat <- factor4_chr10_qtl_effs %>% 
  as.matrix()
# get correlations
factor4_eqtl_effs_corr <- cor(eQTL_at_chr10_peak_effs_mat, factor4_chr10_qtl_effs_mat)
factor4_eqtl_effs_wcorr <- factor4_eqtl_effs_corr %>% 
  as_tibble( rownames = "ensembl_gene_id") %>% 
  rename( "eqtl_cor"= V1) 

# add eQTL lod
# get eQTL lods from scans + add ids
# load scans
load("/projects/munger-lab/projects/DO_mESC/rna_seq/qtl_mapping/total_gene_expression/eqtl_grid69k_pe/DO185_mESC_paired_eQTL_scans.RData") # esc.rna.scans
factor4_eqtl_lods <- apply( esc.rna.scans[c(factor4_chr10_qtl$before, factor4_chr10_qtl$after),],2,max ) %>% 
  as_tibble( rownames = "ensembl_gene_id") %>% 
  rename( eQTL_lod = value)
rm(esc.rna.scans)

# merge all into a data frame
Figure5D_data <- factor4_eqtl_effs_wcorr %>% 
  left_join( factor4_eqtl_lods) %>% 
  left_join( all_df_shared_weights %>% 
               filter( factor == "Factor4", feature %in% all.genes$ensembl_gene_id) %>% 
               select( ensembl_gene_id = feature, transcript_weight = value)
  ) %>% 
  # add gene details
  left_join( ., all.genes2 %>% 
               select( ensembl_gene_id, midpoint, gene_chr, mgi_symbol)) %>% 
  # add mofa qtl details
  cbind(., factor4_chr10_qtl %>% 
          select( factor4_qtl_pos =interp_bp_peak , factor4_qtl_peak_chr = peak_chr)
  ) %>% 
  filter(!is.na(eqtl_cor)) %>% 
  filter(  !(factor4_qtl_peak_chr == gene_chr &
              abs( midpoint - as.numeric(factor4_qtl_pos)) < 10e06 ) ) %>% 
  select(
    `Gene ID` = ensembl_gene_id,
    `MGI symbol` = mgi_symbol,
    `Correlation between QTL effects` = eqtl_cor,
    `LOD score` = eQTL_lod,
    `Transcript weight` = transcript_weight
  )


Figure5D_data %>% 
  filter( `LOD score` < 7.5, 
          abs(`Transcript weight`) >0.1 ) %>% 
  ggplot()+
  aes(
    x = `Transcript weight`,
    y =  `LOD score`,
    col = `Correlation between QTL effects`,
    text = paste("Gene: ", `MGI symbol`) 
  )+
  geom_point(size = 3, alpha = 0.6)+
  theme_pubclean(base_size = 18)+
  scale_color_gradient2( limits = c(-1,1))+
  xlab("Transcript weight")+
  ylab("LOD score")+
  labs( col = "Correlation between\nQTL effects") +
  ylim(0,15)+
  xlim(-0.5, 1)+
  theme(legend.text = element_text(angle = 30), 
        legend.position = "top",
        legend.title = element_text(vjust = 1.3))+
  # add the significant qtl in gray
  geom_point( data = filter(Figure5D_data, `LOD score` > 7.5, abs(`Transcript weight`)>0.1),
              col = "gray", size =3, alpha = 0.6) -> Figure5D

#ggplotly(Figure5D, width = 700, height = 400)
Figure5D
Figure 5D: For all expressed transcripts, the eQTL LOD score at the Chr 10 QTL peak is plotted on the y-axis relative to that transcript’s contribution to Factor 4 on the x-axis. Again, transcripts with absolute factor weights less than 0.1 were filtered, and individual points are as described in panel C. Many transcripts contribute to Factor 4 and have correlated allele effects at the Chr 10 QTL, despite individually failing to map with a significant Chr 10 eQTL.

Figure 5D: For all expressed transcripts, the eQTL LOD score at the Chr 10 QTL peak is plotted on the y-axis relative to that transcript’s contribution to Factor 4 on the x-axis. Again, transcripts with absolute factor weights less than 0.1 were filtered, and individual points are as described in panel C. Many transcripts contribute to Factor 4 and have correlated allele effects at the Chr 10 QTL, despite individually failing to map with a significant Chr 10 eQTL.


Figure5D_data %>% 
  mutate_if( is.numeric, round, 2) %>% 
  create_dt()

Data plotted in Figure 5D.



Figure 5E: Mediation of Factor 4 QTL on chromosome 10

# prep factor 4 qtl for mediation
factor4_chr10_qtl <- factor4_chr10_qtl %>% 
  mutate(pos_cM = as.double(pos)) %>%
  left_join(select(map_dat2, -chr), by = c("pos_cM")) %>%
  filter(!is.na(marker)) %>%
  mutate(phenotype = lodcolumn)

# factor 4 qtl scan
scan1_factor4 <- scan1(genoprobs = shared.probs,
                 pheno =  all_df_shared_factors_mat_rankZ[,"Factor4",drop = FALSE],
                 kinship = shared.kinship,
                 addcovar = shared.covar)

# factor 4 mediation
# phenotype to be mediated  
target <- all_df_shared_factors_mat_rankZ[, "Factor4", drop = FALSE]
# prep ESC transcript mediators & annotations
meds.rna <- tibble(ensembl_gene_id = colnames(expr.esc_rna)) %>%
  left_join(all.genes) %>%
  mutate( id = ensembl_gene_id) %>%
  mutate(chrom = ifelse(gene_chr == "MT", "M", gene_chr)) %>%
  filter(!is.na(chrom))
mediator.rna <- exprZ.esc_rna[threeway.shared.samples$sampleid,
  meds.rna$ensembl_gene_id,
  drop = FALSE
] %>%
  as.data.frame() %>%
  rownames_to_column() %>%
  left_join(., select(threeway.shared.samples, sampleid, top_muga), by = c("rowname" = "sampleid")) %>%
  column_to_rownames("top_muga") %>%
  select(-rowname) %>%
  as.matrix()
# prep ESC protein mediators & annotations
meds.prot <- tibble(protein_id = colnames(exprZ.esc_prot[,all.prots$protein_id])) %>%
  left_join(all.prots) %>%
  mutate( id = protein_id) %>%
  mutate(chrom = ifelse(gene_chr == "MT", "M", gene_chr)) %>%
  mutate(end = gene_end, start = gene_start, id = protein_id)
mediator.prot <- exprZ.esc_prot[threeway.shared.samples$sampleid,
  meds.prot$protein_id,
  drop = FALSE
] %>%
  as.data.frame() %>%
  rownames_to_column() %>%
  left_join(., select(threeway.shared.samples, sampleid, top_muga), by = c("rowname" = "sampleid")) %>%
  column_to_rownames("top_muga") %>%
  select(-rowname) %>%
  as.matrix()
# run mediation using a wrapper function
get_meds <- function(target, meds, mediator, peaks, probs,covar, z_thres = -4,  pos_thres = 10 ){
  med.scan.all <- c()
  samples <- rownames(mediator)
  for( i in 1:nrow(peaks)){
    #print( paste0("i is ", i))
    
    marker    <- map_dat2 %>% filter(pos_cM == peaks$pos_cM[i])
    annot     <- meds %>% mutate(chr=gene_chr,pos=abs(gene_end+gene_start)/2)
    geno      <- pull_genoprobpos(probs,marker$marker)
    geno      <- geno[samples,]
    
    if( !is.null(covar)){ 
      covar <- covar[samples,,drop=FALSE] 
    }
    
    med.scan <- mediation.scan(target= target[samples,peaks$phenotype[i], drop=FALSE],
                               mediator = mediator,
                               annotation = annot,
                               covar =  covar,
                               qtl.geno = geno,
                               verbose = FALSE,
                               method     = "double-lod-diff") 
    
    med.scan <- med.scan %>% 
      mutate(phenotype= peaks$phenotype[i], 
             peak_chr = peaks$peak_chr[i], 
             peak_lod = peaks$lod[i],
             peak_pos_Mbp = peaks$pos_bp/1e06,
             med_chr  = chr) %>% 
      mutate(scaled_LOD = scale(LOD), 
             middle = (gene_end+gene_start)/2e06) %>%
      filter( (#scaled_LOD < z_thres & 
                 peak_chr ==  med_chr & 
                 abs(middle - peak_pos_Mbp) <= pos_thres) &
                 (LOD < peak_lod))
    
    med.scan.all <- rbind(med.scan.all,med.scan)
  }
  return(med.scan.all)
}
factor4_rna_meds <-  get_meds(
  target = target,
  meds = meds.rna,
  mediator = mediator.rna,
  peaks = factor4_chr10_qtl,
  probs = shared.probs,
  covar = shared.covar
)
factor4_prot_meds <-  get_meds(
  target = target,
  meds = meds.prot,
  mediator = mediator.prot,
  peaks = factor4_chr10_qtl,
  probs = shared.probs,
  covar = shared.covar
)

factor4_meds <- factor4_rna_meds %>% 
  mutate( type = "RNA") %>% 
  rbind( factor4_prot_meds %>% 
           mutate( type = "protein")) %>% 
  mutate(
    `Phenotype` = phenotype, 
    `Mediator MGI symbol` = mgi_symbol,
    `QTL chromosome` = peak_chr, 
    `Mediator chromosome` = chr,
    `QTL LOD` = peak_lod,
    `Mediated LOD` = LOD,
    `QTL position (Mbp)`= peak_pos_Mbp, 
    `Mediator midpoint (Mbp)` = middle
  )

scan1_factor4_df <- scan1_factor4 %>% 
  as.data.frame( ) %>% 
  rename( `Factor 4 QTL` = "Factor4") %>% 
  mutate( marker = dimnames(scan1_factor4)[[1]]) %>% 
  left_join(map_dat2) 
scan1_factor4_df$cumsum_pos_bp <- scan1_factor4_df$pos_bp + chrom_lens_offset[scan1_factor4_df$chr]
scan1_factor4_df <- scan1_factor4_df %>% 
  mutate( `Offestted chromosome coordinates` = cumsum_pos_bp,
          marker,
          Chromosome = chr,
          `Coordinates (cM)` = pos_cM, 
          `Coordinates (bp)` = pos_bp) 

Figure5E_data_qtl_scan <- scan1_factor4_df %>% 
  select( `Factor 4 QTL`, 
          `Offestted chromosome coordinates` = cumsum_pos_bp,
          marker,
          Chromosome = chr,
          `Coordinates (cM)` = pos_cM, 
          `Coordinates (bp)` = pos_bp)

Figure5E_data_mediation <- factor4_meds %>% 
  select(
    `Phenotype` = phenotype, 
    `Mediator MGI symbol` = mgi_symbol,
    `QTL chromosome` = peak_chr, 
    `Mediator chromosome` = chr,
    `QTL LOD` = peak_lod,
    `Mediated LOD` = LOD,
    `QTL position (Mbp)`= peak_pos_Mbp, 
    `Mediator midpoint (Mbp)` = middle,
    type
  )
# graphical prep for chromosome locations
chroms <- c(as.character(1:19), "X")
chrom_lens <- c( 195431559, 182107670, 160017104, 156496071, 151833620, 149721874, 145434693, 129399468, 124582650, 130685419, 122078650, 120120622 ,120387272, 124867725, 104015452, 98180002, 94984432, 90672596, 61417310 , 171028300)
names(chrom_lens) <- chroms
chrom_lens_offset <- cumsum(chrom_lens) - chrom_lens
chrom_lens_midpt <- chrom_lens_offset + chrom_lens / 2

# lod plot 
Figure5E_data_qtl_scan %>% 
  ggplot()+
    aes( 
      x= `Offestted chromosome coordinates`,
      y = `Factor 4 QTL`
      )+
  geom_line( size = 1, col = "dark gray",alpha = 0.8)+
  theme_pubclean( base_size = 18)+
  xlab("Chromosome")+
  ylab( "LOD score")+
  scale_x_discrete( name = "Chromosome",
                    limits = chrom_lens_midpt, 
                    labels = names(chrom_lens), 
                    expand = expansion( mult = 0.05)) -> factor4_lod_plot

# overlay mediation
# highlights
factor4_meds_min <- Figure5E_data_mediation %>% 
  slice_min( `Mediated LOD`)
factor4_meds_duxf <- Figure5E_data_mediation %>% 
  filter(`Mediator MGI symbol` == "Duxf3")

factor4_lod_plot+
  geom_point( data = Figure5E_data_mediation, 
              aes( x = `Mediator midpoint (Mbp)`*1e06+chrom_lens_offset[10],
                   y = `Mediated LOD`),
              col = qtl.colors[["rna"]],
              size = 4,
              alpha = 0.9)+
  annotate( "label", 
            y = factor4_meds_min$`Mediated LOD`+0.1, 
            x = factor4_meds_min$`Mediator midpoint (Mbp)`*1e06+chrom_lens_offset[10]-1.9e08 ,
            label = factor4_meds_min$`Mediator MGI symbol`, 
            size = 6, 
            col = qtl.colors[["rna"]],
            fontface = "italic")+
  annotate("label",
           y = factor4_meds_duxf$`Mediated LOD`+0.1, 
            x =factor4_meds_duxf$`Mediator midpoint (Mbp)`*1e06+chrom_lens_offset[10]-1.3e08 ,
            label = factor4_meds_duxf$`Mediator MGI symbol`, 
            size = 6, 
            col = qtl.colors[["rna"]],
            fontface = "italic")+
  ggtitle("MOFA Factor 4 QTL")-> factor4_med_plot

factor4_med_plot
Figure 5E:Genome-wide LOD scores obtained from the Factor 4 QTL scan is plotted with mediation results overlaid. Mediation with Gm20625 transcript abundance causes the largest decrease in QTL LOD score. Duxf3 expression was previously identified as a strong candidate mediator for the eQTL hotspot in this region (Skelly et al., 2020), but performs poorly as a mediator of the Factor 4 QTL compared to Gm20625. Both genes are highlighted in green next to their corresponding LOD score drop.

Figure 5E:Genome-wide LOD scores obtained from the Factor 4 QTL scan is plotted with mediation results overlaid. Mediation with Gm20625 transcript abundance causes the largest decrease in QTL LOD score. Duxf3 expression was previously identified as a strong candidate mediator for the eQTL hotspot in this region (Skelly et al., 2020), but performs poorly as a mediator of the Factor 4 QTL compared to Gm20625. Both genes are highlighted in green next to their corresponding LOD score drop.


QTL scan used in plotting Figure 5E can be downloaded below.

list(Figure5E_data_qtl_scan) %>% 
  downloadthis::download_this(    output_name = "Figure5E data",
    output_extension = ".xlsx",
    button_label = "Download Figure 5E data as xlsx",
    button_type = "primary",
    has_icon = TRUE,
    icon = "fa fa-save"
    )


Figure5E_data_mediation %>% 
  mutate_if(is.numeric, round, 2) %>% 
  arrange(`Mediated LOD`) %>% 
  create_dt()

Mediation results plotted in Figure 5E.



LS0tCnRpdGxlOiAiTXVsdGktb21pY3MgZGF0YSBpbnRlZ3JhdGlvbiIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogNAogICAgdG9jX2Zsb2F0OiAKICAgICAgY29sbGFwc2VkOiBmYWxzZQogICAgICBzbW9vdGhfc2Nyb2xsOiBmYWxzZQogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKLS0tCgo8c3R5bGU+CnAuY2FwdGlvbiB7CiAgZm9udC1zaXplOiAxZW07Cn0KPC9zdHlsZT4KCgpgYGB7ciBzZXR1cH0KCiMgb3B0aW9ucwpvcHRpb25zKHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFKQprbml0cjo6b3B0c19rbml0JHNldChwcm9ncmVzcyA9IEZBTFNFKQoKYGBgCgo8YnI+CgoKV2UgZmVlZCBsb2cgdHJhbnNmb3JtZWQgdmFsdWVzLCB1c2luZyBgbG9nMXAoKWAgYmFzZSBSIGZ1bmN0aW9uLCBmb3IgYWNjZXNzaWJpbGl0eSBhdCAxNSwwMDAgcmVnaW9ucyBpbiB0aGUgZ2Vub21lIHdpdGggdGhlIG1vc3QgdmFyaWFuY2UgaW4gY2hyb21hdGluIGFjY2Vzc2liaWxpdHksIGFsbCB0cmFuc2NyaXB0cyBhbmQgYWxsIHByb3RlaW5zIHdpdGggYWJ1bmRhbmNlIG1lYXN1cmVtZW50cyBpbnRvIE1PRkErIGZvciBtb2RlbCBnZW5lcmF0aW9uLiAKCjwhLS0gQmVsb3cgeW91IGNhbiBmaW5kIHRoZSAuUkRhdGEgZmlsZSBjb250YWluaW5nIHRoZSBkYXRhIGZyYW1lIHdpdGggYWxsIHRoZSBmZWF0dXJlIHZhbHVlcyBwZXIgc2FtcGxlIHJlYWR5IHRvIGJlIGlucHV0dGVkIHRvIE1PRkErLiAtLT4KCmBgYHtyIE1PRkFfZGF0YSwgZWNobyA9IEZBTFNFfQoKI2xpYnJhcnkoTU9GQWRhdGEpCmxpYnJhcnkoTU9GQTIpCiMgbG9hZCBkYXRhIGZyb20gdGhlIE1PRkEgcHJvamVjdApsb2FkKCIvcHJvamVjdHMvbXVuZ2VyLWxhYi9wcm9qZWN0cy9ET19tRVNDL3Byb3Rlb21pY3MvcFFUTF93ZWJzaXRlL19kYXRhL01PRkFfZGF0YV9wcmVwXzA0MTEyMDIyLlJEYXRhIikgIyBoYXMgYWxsIHRoZSBkYXRhIGZyYW1lcwpybShhbGxfZGYsIAogICBhbGxfZGZfdG9wNWssIGFsbF9kZl90b3A1a19zaGFyZWQsCiAgIHRyYW5zX2RmX3RvcDE1aywgdHJhbnNfZGZfdG9wMTVrX3NoYXJlZCwKICAgaGVyaXRfZGZfc2hhcmVkKQoKIyBNT0ZBX2RhdGEgPC0gYWxsX2RmX3NoYXJlZAojIHNhdmUoTU9GQV9kYXRhLCBmaWxlID0gaGVyZSgiTU9GQV9kYXRhLlJEYXRhIikpCgojIHhmdW46OmVtYmVkX2ZpbGUoaGVyZSgiTU9GQV9kYXRhLnppcCIpKQoKCmBgYAoKCjxicj4KPGJyPgoKIyMjIEZpZ3VyZSA1QTogTU9GQSBmYWN0b3JzIG92ZXJ2aWV3CgpgYGB7ciBNT0ZBX21vZGVsX2FuZF9mYWN0b3JfZGV0YWlsc30KCiMgY3JlYXRlIHRoZSBNT0ZBIG9iamVjdAphbGxfZGZfc2hhcmVkX01PRkFvYmplY3QgPC0gY3JlYXRlX21vZmEoYWxsX2RmX3NoYXJlZCkKCiMgTG9hZCBpbiBtb2RlbCByZXN1bHRzIGdlbmVyYXRlZCB1c2luZyBfc3JjL01PRkFfdHJhaW5fbW9kZWxfQ1BVLnIKYWxsX2RmX3NoYXJlZF9tb2RlbCA8LSBsb2FkX21vZGVsKGhlcmUoIi4uL3BRVExfd2Vic2l0ZS9fZGF0YS9hbGxfZGZfc2hhcmVkXzMwZmFjdG9yc18yMDIyLTA0LTExLmhkZjUiKSwgcmVtb3ZlX2luYWN0aXZlX2ZhY3RvcnMgPSBGKQojIFdhcm5pbmcgbWVzc2FnZToKIyBJbiAucXVhbGl0eV9jb250cm9sKG9iamVjdCwgdmVyYm9zZSA9IHZlcmJvc2UpIDoKIyAgIEZhY3RvcihzKSAzIGFyZSBzdHJvbmdseSBjb3JyZWxhdGVkIHdpdGggdGhlIHRvdGFsIG51bWJlciBvZiBleHByZXNzZWQgZmVhdHVyZXMgZm9yIGF0IGxlYXN0IG9uZSBvZiB5b3VyIG9taWNzLiBTdWNoIGZhY3RvcnMgYXBwZWFyIHdoZW4gdGhlcmUgYXJlIGRpZmZlcmVuY2VzIGluIHRoZSB0b3RhbCAnbGV2ZWxzJyBiZXR3ZWVuIHlvdXIgc2FtcGxlcywgKnNvbWV0aW1lcyogYmVjYXVzZSBvZiBwb29yIG5vcm1hbGlzYXRpb24gaW4gdGhlIHByZXByb2Nlc3Npbmcgc3RlcHMuCgojIFJlbW92aW5nIEZhY3RvciAzIHRoYXQgc2hvd3MgaGlnaCBjb3JyZWxhdGlvbiB0byB0aGUgdG90YWwgIyBvZiBleHByZXNzZWQgZmVhdHVyZXMuCmFsbF9kZl9zaGFyZWRfbW9kZWwgPC0gc3Vic2V0X2ZhY3RvcnMoIGFsbF9kZl9zaGFyZWRfbW9kZWwsIGZhY3RvcnMgPSBjKDE6MiwgNDozMCkpCgojIFJlbW92aW5nIEZhY3RvcnMgdGhhdCBkb24ndCBleHBsYWluIGF0IGxlYXN0IDElIG9mIHZhcmlhdGlvbiBpbiBhIGRhdGEgc2V0LiAKYWxsX2RmX3NoYXJlZF9mYWN0b3JfdmFyIDwtIGFsbF9kZl9zaGFyZWRfbW9kZWxAY2FjaGUkdmFyaWFuY2VfZXhwbGFpbmVkJHIyX3Blcl9mYWN0b3IgJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbigpICU+JQogIGZpbHRlcihzaW5nbGVfZ3JvdXAuQ2hyb21hdGluID4gMSB8CiAgICAgICAgICAgc2luZ2xlX2dyb3VwLlByb3RlaW4gPiAxIHwKICAgICAgICAgICBzaW5nbGVfZ3JvdXAuVHJhbnNjcmlwdCA+IDEpICU+JQogIGNvbHVtbl90b19yb3duYW1lcygpCmFsbF9kZl9zaGFyZWRfbW9kZWxfZmlsdGVyZWQgPC0gc3Vic2V0X2ZhY3RvcnMoYWxsX2RmX3NoYXJlZF9tb2RlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhY3RvcnMgPSBhcy5udW1lcmljKGdzdWIoIkZhY3RvciIsIiIscm93bmFtZXMoYWxsX2RmX3NoYXJlZF9mYWN0b3JfdmFyKSkpKQoKIyB1cGRhdGluZyB0aGUgc2V4ZXMgaW4gdGhlIG1ldGFkYXRhIHRvIHVzZSBGZW1hbGUvTWFsZSBpbnN0ZWFkIG9mIDBzIGFuZCAxcy4KbWVyZ2VkX21ldGFkYXRhIDwtIG1lcmdlZC5jb3ZhcjIgJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbigpICU+JQogIHNlbGVjdCgtbGlmcikgJT4lCiAgcmVuYW1lKCJzYW1wbGUiPSJyb3duYW1lIikKc2hhcmVkX3NhbXBsZXNfbWV0YWRhdGEgPC0gYWxsX2RmX3NoYXJlZF9tb2RlbF9maWx0ZXJlZEBzYW1wbGVzX21ldGFkYXRhICU+JQogIGxlZnRfam9pbiguLG1lcmdlZF9tZXRhZGF0YSkgJT4lCiAgbXV0YXRlKHNleCA9IGlmZWxzZShzZXggPT0wLCJGZW1hbGUiLCAiTWFsZSIpKQpzYW1wbGVzX21ldGFkYXRhKGFsbF9kZl9zaGFyZWRfbW9kZWxfZmlsdGVyZWQpIDwtIHNoYXJlZF9zYW1wbGVzX21ldGFkYXRhCgoKIyBHZXQgJSB2YXJpYW5jZSBleHBsYWluZWQgZm9yIGVhY2ggZmFjdG9yCmFsbF9kZl9zaGFyZWRfdmFyX2V4cGxhaW5lZCA8LSAoY2FsY3VsYXRlX3ZhcmlhbmNlX2V4cGxhaW5lZChhbGxfZGZfc2hhcmVkX21vZGVsX2ZpbHRlcmVkKSkKCiMgR2V0IEZhY3RvciB3ZWlnaHRzCmFsbF9kZl9zaGFyZWRfZmFjdG9ycyA8LSBnZXRfZmFjdG9ycyhhbGxfZGZfc2hhcmVkX21vZGVsX2ZpbHRlcmVkLAogIGZhY3RvcnMgPSAiYWxsIiwKICBhcy5kYXRhLmZyYW1lID0gVAopCiMgQWRkIHNhbXBsZSBkZXRhaWxzIGFuZCBjb252ZXJ0IHRvIG1hdHJpeCB3aXRoIEZhY3RvcnMgaW4gcm93cyBhbmQgc2FtcGxlcyBpbiBjb2x1bW5zLgphbGxfZGZfc2hhcmVkX2ZhY3RvcnNfbWF0IDwtIGFsbF9kZl9zaGFyZWRfZmFjdG9ycyAlPiUKICBwaXZvdF93aWRlcihpZF9jb2xzID0gInNhbXBsZSIsIG5hbWVzX2Zyb20gPSAiZmFjdG9yIiwgdmFsdWVzX2Zyb20gPSAidmFsdWUiKSAlPiUKICBsZWZ0X2pvaW4oLiwgc2VsZWN0KHRocmVld2F5LnNoYXJlZC5zYW1wbGVzLCB0b3BfbXVnYSwgc2FtcGxlaWQpLCBieSA9Yygic2FtcGxlIj0idG9wX211Z2EiKSkgJT4lCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJzYW1wbGUiKSAlPiUKICBzZWxlY3QoLXNhbXBsZWlkKSAlPiUKICBhcy5tYXRyaXgoKQoKIyBHZXQgZmVhdHVyZSB3ZWlnaHRzCmFsbF9kZl9zaGFyZWRfd2VpZ2h0cyA8LSBnZXRfd2VpZ2h0cyhhbGxfZGZfc2hhcmVkX21vZGVsX2ZpbHRlcmVkLCBhcy5kYXRhLmZyYW1lID0gVFJVRSwgc2NhbGUgPSBUUlVFKQoKIyBDb3JyZWxhdGUgRmFjdG9ycyB3aXRoIGNvdmFyaWF0ZXMgYW5kIGdldCBSIHZhbHVlcwpjb3JyZWxhdGVfZmFjdG9yc193aXRoX2NvdmFyaWF0ZXMoYWxsX2RmX3NoYXJlZF9tb2RlbF9maWx0ZXJlZCwKICBjb3ZhcmlhdGVzID0gYygic2V4IiwibGlmcl9nZW5vIiksCiAgcGxvdD0iciIsCiAgcmV0dXJuX2RhdGEgPSBUUlVFKSAlPiUKICBhc190aWJibGUoIHJvd25hbWVzID0gIkZhY3RvciIpICU+JQogIHBpdm90X2xvbmdlciggY29scyA9IGMoInNleCIsImxpZnJfZ2VubyIpLAogICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiY292YXJpYXRlIiwKICAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJwZWFyc29uX3IiKSAtPiBjb3JyX3RvX2Nvdl9yCgojIENvcnJlbGF0ZSBGYWN0b3JzIHdpdGggY292YXJpYXRlcyBhbmQgZ2V0IHAtdmFsdWVzCmNvcnJlbGF0ZV9mYWN0b3JzX3dpdGhfY292YXJpYXRlcyhhbGxfZGZfc2hhcmVkX21vZGVsX2ZpbHRlcmVkLAogIGNvdmFyaWF0ZXMgPSBjKCJzZXgiLCJsaWZyX2dlbm8iKSwKICBwbG90PSJsb2dfcHZhbCIsCiAgcmV0dXJuX2RhdGEgPSBUUlVFKSAlPiUKICBhc190aWJibGUoIHJvd25hbWVzID0gIkZhY3RvciIpICU+JQogIHBpdm90X2xvbmdlciggY29scyA9IGMoInNleCIsImxpZnJfZ2VubyIpLAogICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiY292YXJpYXRlIiwKICAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJsb2dfcHZhbCIpIC0+IGNvcnJfdG9fY292X2xvZ3B2YWwKCiMgbWVyZ2UgUiAmIHAtdmFsdWVzIGludG8gYSBzaW5nbGUgZGF0YSBmcmFtZQpjb3JyX3RvX2Nvdl9yICU+JQogIGZ1bGxfam9pbiggY29ycl90b19jb3ZfbG9ncHZhbCkgLT4gYWxsX2RmX3NoYXJlZF9jb3JyX2NvdmFyCgoKIyBnZXQgYXRhYy1zZXEgcGVhayBkcml2ZXJzICsgcnVuIG92ZXJyZXByZXNlbnRhdGlvbiBhbmFseXNpcyB1c2luZyBMT0xBCgojIHRoZXNlIGFyZSBhbGwgdGhlIEFUQUMtc2VxIHBlYWtzIHRoYXQgYXJlIGZlZCBpbnRvIE1PRkEgdGhhdCB3ZSB3aWxsIHVzZSBhcyB0aGUgY3VzdG9tIGJhY2tncm91bmQgaW4gb3ZlcnJlcHJlc2VudGF0aW9uIGFuYWx5c2lzCmJhY2tncm91bmRfYXRhY19wZWFrcyA8LSAgYWxsX2RmX3NoYXJlZCAlPiUKICBmaWx0ZXIodmlldyA9PSBjKCJDaHJvbWF0aW4iKSkgICU+JQogIHJlbmFtZSggcGVha19pZCA9IGZlYXR1cmUpICU+JQogIHNlbGVjdCggLXZpZXcsLXZhbHVlLCAtc2FtcGxlKSAlPiUKICBkaXN0aW5jdCgpICU+JQogIHNlcGFyYXRlKCBwZWFrX2lkLCBpbnRvID0gYygiQ2hyIiwgIlN0YXJ0IiwiRW5kIiksIHJlbW92ZSA9IEZBTFNFKSAlPiUKICBtdXRhdGUoIENociA9IGdzdWIoInBlYWsiLCJjaHIiLENocikpICU+JQogIG1ha2VHUmFuZ2VzRnJvbURhdGFGcmFtZSguLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAga2VlcC5leHRyYS5jb2x1bW5zID0gRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcW5hbWVzLmZpZWxkID0gYygiQ2hyIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGFydC5maWVsZCA9ICJTdGFydCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbmQuZmllbGQgPSAiRW5kIikKCmdldF90Zl9vcmEgPC0gZnVuY3Rpb24oIGZhY3Rvcl9kZiwgZmFjdG9yX251bSwgYmdfcGVha3MpewogIAogICMgZ2V0IHRvcCBBVEFDLXNlcSBwZWFrIGRyaXZlcnMKICB0b3BfYXRhY19kcml2ZXJzIDwtIGZhY3Rvcl9kZiAlPiUKICAgIHNsaWNlKCB3aGljaCggZmFjdG9yX2RmJHZhbHVlICVpbiUKICAgICAgICBib3hwbG90LnN0YXRzKCBmaWx0ZXIoZmFjdG9yX2RmLCBmYWN0b3IgPT0gZmFjdG9yX251bSAmIHZpZXcgPT0iQ2hyb21hdGluIikkdmFsdWUpJG91dCApCiAgICApCiAgCiAgIyBjb252ZXJ0IHRoZSB0b3AgQVRBQy1zZXEgcGVhayBkcml2ZXJzIHRvIGEgY29tcGF0aWJsZSBvYmplY3QgZm9yIExPTEEKICBhdGFjX3BlYWtzX2Zvcl9sb2xhIDwtIHRvcF9hdGFjX2RyaXZlcnMgJT4lCiAgICByZW5hbWUoIHBlYWtfaWQgPSBmZWF0dXJlKSAlPiUKICAgIHNlcGFyYXRlKCBwZWFrX2lkLCBpbnRvID0gYygiQ2hyIiwgIlN0YXJ0IiwiRW5kIiksIHJlbW92ZSA9IEZBTFNFKSAlPiUKICAgIG11dGF0ZSggQ2hyID0gZ3N1YigicGVhayIsImNociIsQ2hyKSkgJT4lCiAgICBtYWtlR1Jhbmdlc0Zyb21EYXRhRnJhbWUoLiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtlZXAuZXh0cmEuY29sdW1ucyA9IEYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXFuYW1lcy5maWVsZCA9IGMoIkNociIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhcnQuZmllbGQgPSAiU3RhcnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5kLmZpZWxkID0gIkVuZCIpCiAgIyBydW4gTE9MQSB3aXRoIGN1c3RvbSBiYWNrZ3JvdW5kCiAgbG9sYV9yZXN1bHRzIDwtIHJ1bkxPTEEoYXRhY19wZWFrc19mb3JfbG9sYSwKICAgICAgICAgICAgICAgICAgICAgICAgICBiZ19wZWFrcywKICAgICAgICAgICAgICAgICAgICAgICAgICByZWdpb25EQiwKICAgICAgICAgICAgICAgICAgICAgICAgICBjb3Jlcz0xKQogICMgYWRkIHEtdmFsdWUgZm9yIGZpbHRlcmluZyBsYXRlcgogIGxvbGFfcmVzdWx0cyRxVmFsdWUgPC0gKHF2YWx1ZSggMTBeKC1sb2xhX3Jlc3VsdHMkcFZhbHVlTG9nICkpKSRxdmFsdWVzCgogIHJldHVybihsb2xhX3Jlc3VsdHMpCiAgCn0KCiMgZ2V0X3RmX29yYSggYWxsX2RmX3NoYXJlZF93ZWlnaHRzLCAiRmFjdG9yMSIsIGJhY2tncm91bmRfYXRhY19wZWFrcykKIyBJIHJhbiB0aGUgZnVuY3Rpb24gYWJvdmUgZm9yIGFsbCBmYWN0b3JzIGFuZCBzYXZlZCB0aGUgb3V0cHV0CiMgYmVsb3cgaXMgdGhlIGZpbHRlcmVkIHJlc3VsdHMgZm9yIHF2YWx1ZSA8MC4wNSBhbmQgY2VsbCB0eXBlIGFzIEVTQ3MKbG9hZCggaGVyZSgiLi4vcFFUTF93ZWJzaXRlL19kYXRhLyIsIk1PRkFfRmFjdG9yX0xPTEFfcmVzdWx0cy5SRGF0YSIpKSAjIGxvbGFfY29tYmluZWQKCmBgYAo8YnI+CgpgYGB7ciBGaWd1cmVfNUFfcHJlcH0KCkZpZ3VyZTVBX2RhdGFfdmFyX2V4cCA8LSBhbGxfZGZfc2hhcmVkX3Zhcl9leHBsYWluZWQkcjJfcGVyX2ZhY3RvciRzaW5nbGVfZ3JvdXAgJT4lCiAgYXNfdGliYmxlKHJvd25hbWVzID0gIkZhY3RvciIpICU+JQogIG11dGF0ZShmYWN0b3JfbnVtID0gYXMubnVtZXJpYyhnc3ViKCAiRmFjdG9yIiAsIiIsRmFjdG9yKSkgKSU+JQogIGFycmFuZ2UoKGZhY3Rvcl9udW0pKSAlPiUKICBwaXZvdF9sb25nZXIoYygiQ2hyb21hdGluIiwiUHJvdGVpbiIsIlRyYW5zY3JpcHQiKSkgJT4lCiAgbXV0YXRlKCBGYWN0b3IgPSBmYWN0b3IoRmFjdG9yLCBsZXZlbHMgPSB1bmlxdWUoRmFjdG9yKSkpICU+JQogIG11dGF0ZSggbmFtZSA9IGZhY3RvciggbmFtZSwgbGV2ZWxzID0gYygiQ2hyb21hdGluIiwiVHJhbnNjcmlwdCIsIlByb3RlaW4iKSkpICU+JQogIHNlbGVjdCggYERhdGEgc2V0YCA9IG5hbWUsIEZhY3RvciwgYCUgVmFyaWF0aW9uIEV4cGxhaW5lZGAgPSB2YWx1ZSkKCkZpZ3VyZTVBX2RhdGFfY29yX3RvX2NvdmFyIDwtIGFsbF9kZl9zaGFyZWRfY29ycl9jb3ZhciAlPiUKICBtdXRhdGUoIEZhY3RvciA9IGZhY3RvcihGYWN0b3IsIGxldmVscyA9IHVuaXF1ZShGYWN0b3IpKSkgJT4lCiAgbXV0YXRlKCBjb3ZhcmlhdGUgPSBpZmVsc2UoIGNvdmFyaWF0ZSA9PSJzZXgiLCAiU2V4IiwgIkxJRlIgZ2Vub3R5cGUiKSkgJT4lCiAgbXV0YXRlKCBjb3ZhcmlhdGUgPSBmYWN0b3IoIGNvdmFyaWF0ZSwgbGV2ZWxzID0gYygiU2V4IiwgIkxJRlIgZ2Vub3R5cGUiKSkpICU+JQogIHNlbGVjdChGYWN0b3IsIGNvdmFyaWF0ZSwgQ29ycmVsYXRpb24gPXBlYXJzb25fcikKCkZpZ3VyZTVBX2RhdGFfdGZfb3JhIDwtIGxvbGFfY29tYmluZWQgJT4lIAogIGZpbHRlciggYW50aWJvZHkgJWluJSBjKCJOYW5vZyIsIlNveDIiLCJQb3U1ZjEiKSkgJT4lIAogIGZpbHRlciggZGVzY3JpcHRpb24gJWluJSBjKCJFUyBjZWxscyBleHByZXNzaW5nIGNvbnRyb2wgc2hSTkEgdGFyZ2V0aW5nIEdGUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlY2LjUgKEM1N0JMLzYtMTI5KSBtdXJpbmUgRVMgY2VsbHMgd2VyZSBncm93biB1bmRlciB0eXBpY2FsIEVTIGNvbmRpdGlvbnMgb24gaXJyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTi9BIikpICU+JSAKICBmaWx0ZXIoICFkZXNjcmlwdGlvbiAlaW4lIGMoIkVTIGNlbGxzIGV4cHJlc3Npbmcgc2hSTkEgdGFyZ2V0aW5nIEtkbTRiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRVMgY2VsbHMgZXhwcmVzc2luZyBzaFJOQSB0YXJnZXRpbmcgS2RtNGMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFUyBjZWxscyBjdWx0dXJlZCBpbiBGR0Y0OyBoZXBhcmluIGFuZCBBY3RpdmluIEEgZm9yIDEgZGF5IGFmdGVyIE9jdDMvNCBkZWxldGVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRVMgY2VsbHMgZGlmZmVyZW50aWF0ZWQgaW4gdml0cm8gZm9yIDIgZGF5czsgQnJuMiBleHByZXNzaW9uIGluZHVjZWQgdXNpbmcgVGV0LU8iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFUyBjZWxscyBjdWx0dXJlZCBpbiBGR0Y0OyBoZXBhcmluIGFuZCBBY3RpdmluIEEgZm9yIDIgZGF5cyBhZnRlciBPY3QzLzQgZGVsZXRlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkVTIGNlbGxzIGN1bHR1cmVkIGluIEZHRjQ7IGhlcGFyaW4gYW5kIEFjdGl2aW4gQSBmb3IgMyBkYXlzIGFmdGVyIE9jdDMvNCBkZWxldGVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRVMgY2VsbHMgZGlmZmVyZW50aWF0ZWQgaW4gdml0cm8gZm9yIDIgZGF5cyIpKSAlPiUgCiAgZnVsbF9qb2luKCB0aWJibGUoCiAgICAgICAgICAgICAgICBmYWN0b3IgPSBjKCBwYXN0ZTAoIkZhY3RvciAiLCBzZXEoMToyMykpKSwKICAgICAgICAgICAgICAgIGZhY3Rvcl9udW0gPSBzZXEoMToyMykpIAogICAgICAgICAgICAgICkgJT4lIAogIGdyb3VwX2J5KGZhY3RvciwgYW50aWJvZHksIGZhY3Rvcl9udW0pICU+JSAKICBkcGx5cjo6c3VtbWFyaXplKCBtZWFuX29kZHMgPSBtZWFuKG9kZHNSYXRpbywgbmEucm09IFQpKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBhcnJhbmdlKGZhY3Rvcl9udW0pICU+JSAKICBtdXRhdGUoIEZhY3RvciA9IGZhY3RvcihmYWN0b3IsIGxldmVscyA9IHVuaXF1ZShmYWN0b3IpKSkgJT4lCiAgc2VsZWN0KEZhY3RvciwgVEYgPSBhbnRpYm9keSwgYE9kZHMgcmF0aW9gID0gbWVhbl9vZGRzKQogIAogIApgYGAKCmBgYHtyIEZpZ3VyZV81QV9wbG90LCBmaWcuY2FwID0gIk1PRkEgeWllbGRlZCAyMyBsYXRlbnQgZmFjdG9ycyB0aGF0IGNhcHR1cmUgdmFyaWF0aW9uIGluIG9uZSBvciBtb3JlIGxheWVycyBvZiBnZW5vbWljIGRhdGEuIEZvciBlYWNoIGZhY3RvciwgcGVyY2VudCBvZiB2YXJpYXRpb24gZXhwbGFpbmVkIGluIGNocm9tYXRpbiBhY2Nlc3NpYmlsaXR5LCB0cmFuc2NyaXB0IGFidW5kYW5jZSwgYW5kIHByb3RlaW4gYWJ1bmRhbmNlIGlzIGRpc3BsYXllZCBhcyBhIGhlYXRtYXAsIGFzIGlzIHRoZSBjb3JyZWxhdGlvbiBvZiBlYWNoIGZhY3RvciB0byBleHBlcmltZW50YWwgY292YXJpYXRlcyBpbmNsdWRpbmcgc2V4IGFuZCBnZW5vdHlwZSBhdCB0aGUgTGlmciBsb2N1cy4gSGVhdG1hcCBvbiB0aGUgcmlnaHQgaW5kaWNhdGVzIG92ZXJyZXByZXNlbnRhdGlvbiBvZiBwbHVyaXBvdGVuY3kgcmVndWxhdG9yIGJpbmRpbmcgc2l0ZXMgKE5BTk9HLCBPQ1Q0IChQb3U1ZjEpIGFuZCBTT1gyKSBhbW9uZyB0aGUgdG9wIGNocm9tYXRpbiBkcml2ZXJzIG9mIGVhY2ggZmFjdG9yLiIsIGZpZy53aWR0aD0xNiwgZmlnLmhlaWdodD04fQoKCiMgJSB2YXJpYXRpb24gZXhwbGFpbmVkIGhlYXRtYXAuCkZpZ3VyZTVBX2RhdGFfdmFyX2V4cCAlPiUgCiAgZ2dwbG90KCkrCiAgYWVzKCAKICAgICAgIHkgPSBGYWN0b3IsCiAgICAgICB4ID0gYERhdGEgc2V0YCwKICAgICAgIGZpbGwgPSBgJSBWYXJpYXRpb24gRXhwbGFpbmVkYCkrCiAgZ2VvbV90aWxlKCkrCiAgeGxhYigiIikrCiAgeWxhYigiIikrCiAgdGhlbWVfcHViY2xlYW4oIGJhc2Vfc2l6ZSA9IDE4KSsKICBzY2FsZV9maWxsX2dyYWRpZW50MiggbGltaXRzID0gYygwLCAxMCkpKwogICNzY2FsZV9zaXplX2FyZWEoKSsKICBsYWJzKGZpbGwgPSAiJSBWYXJpYXRpb24gRXhwbGFpbmVkIiwgY29sID0gIiIpKwogIHNjYWxlX3hfZGlzY3JldGUoIGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gMCkpKwogIHRoZW1lKAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiLCAKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4LCB2anVzdCA9IDAuOSksCiAgICAgICAgbGVnZW5kLnRleHQgID0gZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBzaXplID0xMiwgdmp1c3QgPSAwLjUpLAogICAgICAgICktPiBhbGxfZGZfc2hhcmVkX3Zhcl9wbG90CgojIGNvcnJlbGF0aW9uIHRvIGNvdmFyaWF0ZXMuCkZpZ3VyZTVBX2RhdGFfY29yX3RvX2NvdmFyICU+JQogIGZpbHRlcihjb3ZhcmlhdGUgPT0gIlNleCIpICU+JSAKICBnZ3Bsb3QoKSsKICBhZXMoCiAgICB4ID0gY292YXJpYXRlLAogICAgeSA9IEZhY3RvciwKICAgIGZpbGwgPSBhYnMoQ29ycmVsYXRpb24pCiAgKSsKICBnZW9tX3RpbGUoKSsKICB4bGFiKCIiKSsKICB5bGFiKCIiKSsKICB0aGVtZV9wdWJjbGVhbihiYXNlX3NpemUgPSAxOCkrCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAid2hpdGUiLGhpZ2ggPSAiZGFyayByZWQiLCBsaW1pdHMgPSBjKDAsMSkpKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiLAogICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQudGV4dCAgPSBlbGVtZW50X3RleHQoYW5nbGU9NDUsIHNpemUgPTEyLCB2anVzdCA9IDAuNSksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCwgdmp1c3QgPSAwLjkpLAogICAgICAgICkrCiAgc2NhbGVfeV9kaXNjcmV0ZSggbGFiZWxzID0gTlVMTCkrCiAgc2NhbGVfeF9kaXNjcmV0ZSggZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSAwKSkrCiAgbGFicyggZmlsbCA9ICJDb3JyZWxhdGlvbiIpIC0+IGFsbF9kZl9zaGFyZWRfY292YXJfcGxvdF9zZXgKCkZpZ3VyZTVBX2RhdGFfY29yX3RvX2NvdmFyICU+JQogIGZpbHRlcihjb3ZhcmlhdGUgIT0gIlNleCIpICU+JSAKICBnZ3Bsb3QoKSsKICBhZXMoCiAgICB4ID0gY292YXJpYXRlLAogICAgeSA9IEZhY3RvciwKICAgIGZpbGwgPSBhYnMoQ29ycmVsYXRpb24pCiAgKSsKICBnZW9tX3RpbGUoKSsKICB4bGFiKCIiKSsKICB5bGFiKCIiKSsKICB0aGVtZV9wdWJjbGVhbihiYXNlX3NpemUgPSAxOCkrCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAid2hpdGUiLGhpZ2ggPSAiZGVlcHBpbms0IiwgbGltaXRzID0gYygwLDEpKSsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIiwKICAgICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnRleHQgID0gZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBzaXplID0xMiwgdmp1c3QgPSAwLjUpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0xMikKICAgICAgICApKwogIHNjYWxlX3lfZGlzY3JldGUoIGxhYmVscyA9IE5VTEwpKwogIHNjYWxlX3hfZGlzY3JldGUoIGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gMCkpKwogIGxhYnMoIGZpbGwgPSAiIikgLT4gYWxsX2RmX3NoYXJlZF9jb3Zhcl9wbG90X2xpZnIKCiMgT2RkcyByYXRpbyBmb3IgVEYgYmluZGluZyBvdmVycmVwcmVzZW50YXRpb24gZm9yIE5BTk9HLCBPQ1Q0LCBTT1gyIGluIEFUQUNzZXEgcGVhayBkcml2ZXJzCkZpZ3VyZTVBX2RhdGFfdGZfb3JhICU+JSAKICBtdXRhdGUoIG1lYW5fb2Rkc19jYXBwZWQgPSBpZmVsc2UoIGBPZGRzIHJhdGlvYCA+MTAsIDEwLCBgT2RkcyByYXRpb2ApKSAlPiUgCiAgZ2dwbG90KCkrCiAgYWVzKAogICAgeCA9IFRGLAogICAgeSA9IEZhY3RvciwKICAgIGZpbGwgPSBtZWFuX29kZHNfY2FwcGVkCiAgKSsKICBnZW9tX3RpbGUoKSsKICB4bGFiKCIiKSsKICB5bGFiKCIiKSsKICB0aGVtZV9wdWJjbGVhbihiYXNlX3NpemUgPSAxOCkrCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAibGlnaHQgZ3JheSIsaGlnaCA9ICIjZmZhMDhhZmYiLCBsaW1pdHMgPSBjKDAsMTApKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIsCiAgICAgICAgbGVnZW5kLnRleHQgID0gZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBzaXplID0xMiwgdmp1c3QgPSAwLjUpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgsIHZqdXN0ID0gMC45KSwKICAgICAgICBheGlzLnRleHQueCA9ICBlbGVtZW50X3RleHQoYW5nbGUgPSAwKSwKICAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICkrCiAgc2NhbGVfeV9kaXNjcmV0ZSggbGFiZWxzID0gTlVMTCkrCiAgc2NhbGVfeF9kaXNjcmV0ZSggZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSAwKSkrCiAgbGFicyggZmlsbCA9ICJPZGRzIHJhdGlvIikgLT4gbG9sYV9zdW1fZmlndXJlCgpmaWd1cmU1YSA8LSBnZ2FycmFuZ2UoIGFsbF9kZl9zaGFyZWRfdmFyX3Bsb3QsCiAgICAgICAgICAgICAgICAgICAgICAgYWxsX2RmX3NoYXJlZF9jb3Zhcl9wbG90X3NleCwKICAgICAgICAgICAgICAgICAgICAgICBhbGxfZGZfc2hhcmVkX2NvdmFyX3Bsb3RfbGlmciwKICAgICAgICAgICAgICAgICAgICAgICBsb2xhX3N1bV9maWd1cmUsCiAgICAgICAgICAgICAgICAgICAgICAgd2lkdGhzID0gYygwLjQ1LDAuMTUsMC4xNSwwLjIpLCBucm93ID0gMSwKICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSAiQSIsCiAgICAgICAgICAgICAgICAgICAgICAgZm9udC5sYWJlbCA9IGxpc3QoIHNpemUgPSAyOCkpCgoKZmlndXJlNWEKCmBgYAoKPGJyPgoKYGBge3IgRmlndXJlXzVBX2RhdGFfdmFyaWF0aW9uX2V4cGxhaW5lZH0KCkZpZ3VyZTVBX2RhdGFfdmFyX2V4cCAlPiUgCiAgbXV0YXRlX2lmKCBpcy5udW1lcmljLCByb3VuZCwgMikgJT4lIAogIGNyZWF0ZV9kdCgpCgpgYGAKCjxicj4KCmBgYHtyIEZpZ3VyZV81QV9kYXRhX2NvcnJlbGF0aW9uX3RvX2NvdmFyaWF0ZXN9CgpGaWd1cmU1QV9kYXRhX2Nvcl90b19jb3ZhciAlPiUgCiAgbXV0YXRlX2lmKCBpcy5udW1lcmljLCByb3VuZCwgMikgJT4lIAogIGNyZWF0ZV9kdCgpCgpgYGAKCjxicj4KCmBgYHtyIEZpZ3VyZV81QV9kYXRhX3RmX292ZXJyZXByZXNlbnRhdGlvbn0KCkZpZ3VyZTVBX2RhdGFfdGZfb3JhICU+JSAKICBtdXRhdGVfaWYoIGlzLm51bWVyaWMsIHJvdW5kLCAyKSAlPiUgCiAgY3JlYXRlX2R0KCkKCmBgYAoKPGJyPgoKIyMjIyBUYWJsZSBTNzogTU9GQSBmYWN0b3IgdmFsdWVzIGZvciBzYW1wbGVzIGFuZCBsaXN0cyBvZiBtb2xlY3VsYXIgZmVhdHVyZXMgd2l0aCB0aGVpciB3ZWlnaHRzIGZvciBlYWNoIG9mIDIzIE1PRkEgZmFjdG9ycy4KCk1PRkEgZmFjdG9yIHZhbHVlcyAoY29sdW1ucykgZm9yIGVhY2ggb2YgdGhlIDE2MyBzYW1wbGVzIChyb3dzKSBhcmUgbGlzdGVkIGluIHRoZSBmaXJzdCB0YWIuIFdlaWdodHMgZm9yIGVhY2ggb2YgdGhlIDIzIE1PRkEgZmFjdG9ycyAoY29sdW1ucykgYXJlIGxpc3RlZCBmb3IgZXZlcnkgcHJvdGVpbiwgdHJhbnNjcmlwdCwgYW5kIGNocm9tYXRpbiBwZWFrIChyb3dzKSwgd2l0aCB0YWJzIGNvcnJlc3BvbmRpbmcgdG8gdGhlIHRocmVlIGRhdGEgdHlwZXMuRmVhdHVyZSBhbm5vdGF0aW9ucyBpbmNsdWRlIEVuc2VtYmwgcHJvdGVpbiBpZGVudGlmaWVyIGFuZCBnZW5lIHN5bWJvbCBmb3IgcHJvdGVpbnMsIEVuc2VtYmwgZ2VuZSBpZGVudGlmaWVyIGFuZCBnZW5lIHN5bWJvbCBmb3IgdHJhbnNjcmlwdHMsIGFuZCBjaHJvbWF0aW4gcGVhayBpZGVudGlmaWVyIGFuZCBwcm94aW1hbCBnZW5lIGFubm90YXRpb24gZnJvbSBSL0NoSVBzZWVrZXIgZm9yIEFUQUMtc2VxIHBlYWtzLgoKCmBgYHtyIHRhYmxlX1M3X2VtYmVkLCBlY2hvID0gRkFMU0V9CgojIHhmdW46OmVtYmVkX2ZpbGUoaGVyZSgiVGFibGVfUzcueGxzeCIpKQoKZG93bmxvYWRfZmlsZSgKICBwYXRoID0gaGVyZSgiVGFibGVfUzcueGxzeCIpLAogIG91dHB1dF9uYW1lID0gIlRhYmxlX1M3IiwKICBidXR0b25fbGFiZWwgPSAiRG93bmxvYWQgVGFibGVfUzcueGxzeCIsCiAgYnV0dG9uX3R5cGUgPSAicHJpbWFyeSIsCiAgaGFzX2ljb24gPSBUUlVFLAogIGljb24gPSAiZmEgZmEtc2F2ZSIsCiAgc2VsZl9jb250YWluZWQgPSBGQUxTRQopCgpgYGAKCgo8YnI+CgojIyMjIEZpZ3VyZSBTNUE6IERyaXZlcnMgb2YgTU9GQSBmYWN0b3JzLgoKYGBge3IgRmlndXJlX1M1QSwgZmlnLmhlaWdodD03LCBmaWcud2lkdGg9NSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZmlnLmNhcCA9ICJGaWd1cmUgUzUuIEhlYXRtYXBzIHNob3dpbmcgdGhlIG51bWJlciBvZiBmZWF0dXJlcyBpbiBlYWNoIGRhdGEgc2V0IHdpdGggYWJzKHdlaWdodCkgPiAwLjAxIGZvciAyMyBNT0ZBIEZhY3RvcnMuIn0KCmFsbF9kZl9zaGFyZWRfd2VpZ2h0cyAlPiUgCiAgZmlsdGVyKCBhYnModmFsdWUpID4gMC4wMSkgJT4lCiAgI2dyb3VwX2J5KHZpZXcpICU+JSBzdW1tYXJpc2UoIHZhbHVlID0gbWVkaWFuKHZhbHVlKSkKICBncm91cF9ieShmYWN0b3IpICU+JSAKICBjb3VudCggdmlldywgLmRyb3AgPSBGQUxTRSkgJT4lCiAgZ2dwbG90KCkrCiAgYWVzKAogICAgeCA9IHZpZXcsCiAgICB5ID0gZmFjdG9yLAogICAgZmlsbCA9IG4KICAgICkrCiAgZ2VvbV90aWxlKCkrCiAgeGxhYigiIikrCiAgeWxhYigiIikrCiAgdGhlbWVfcHViY2xlYW4oIGJhc2Vfc2l6ZSA9IDE4KSsKICAjc2NhbGVfY29sb3JfdmlyaWRpc19jKG9wdGlvbiA9ICJDIikrCiAgc2NhbGVfZmlsbF9ncmFkaWVudDIobGltaXRzID0gYygwLCAxNTAwMCkpKwogIGxhYnMoZmlsbCA9ICIjIG9mIGZlYXR1cmVzIiwgY29sID0gIiIpKwogIHNjYWxlX3hfZGlzY3JldGUoIGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gMCkpKwogIHRoZW1lKAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiLCAjYygwLjc1LDAuOTUpLAogICAgICAgICNheGlzLmxpbmUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4LCB2anVzdCA9IDAuOSksCiAgICAgICAgbGVnZW5kLnRleHQgID0gZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBzaXplID0xMiwgdmp1c3QgPSAwLjUpLAogICAgICAgICkgCgpgYGAKCgo8YnI+Cjxicj4KCgojIyMgRmlndXJlIDVCOiBRVEwgbWFwcGluZyB3aXRoIE1PRkEgRmFjdG9ycwoKYGBge3IgRmlndXJlNWIsIGZpZy5jYXAgPSAiRmlndXJlIDVCOiBBYm92ZSBzaG93cyBhIGRlcGljdGlvbiBvZiBRVEwgbWFwcGluZyB3aXRoIE1PRkEgZmFjdG9ycyB0byBpZGVudGlmeSB0aGUgZ2VuZXRpYyBtb2RpZmllcnMgZHJpdmluZyB2YXJpYXRpb24gYWNyb3NzIHRocmVlIG1vbGVjdWxhciBsYXllcnMuIEJlbG93IGlzIGEgdGFibGUgb2YgUVRMIHBlYWtzIHRoYXQgbWFwIGFib3ZlIHRoZSBnZW5vbWUtd2lkZSBzaWduaWZpY2FuY2UgdGhyZXNob2xkIGNhbGN1bGF0ZWQgaW5kaXZpZHVhbGx5IGZvciBlYWNoIGZhY3Rvci4gTG9jaSB0aGF0IHdlcmUgcHJldmlvdXNseSBvYnNlcnZlZCBhcyBtb2xlY3VsYXIgUVRMIGhvdHNwb3RzIGFyZSBkZW5vdGVkIGluIHRoZSDigJxUeXBl4oCdIGNvbHVtbi4gIiwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgb3V0LndpZHRoPSI0NTBweCJ9CgojIGxvYWQgZnJvbSBmaWxlCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmUoIkZpZ3VyZTVCLnBuZyIpKQoKYGBgCgoKPGJyPgoKYGBge3IgZGF0YV9mb3JfZmlndXJlNUIsIGZpZy5jYXAgPSAiRGF0YSBzaG93biBpbiBGaWd1cmUgNUIuIn0KCiMgcmFua1ogdHJhbnNmb3JtIGJlZm9yZSBtYXBwaW5nCmFsbF9kZl9zaGFyZWRfZmFjdG9yc19tYXRfcmFua1ogPC0gYXBwbHkoIGFsbF9kZl9zaGFyZWRfZmFjdG9yc19tYXQsIDIsIHJhbmtaKQoKCgojIHJ1biBxdGwgc2NhbnMgd2l0aCBzZXggYXMgYW4gYWRkaXRpdmUgY292YXJpYXRlCmFsbF9kZl9zY2FucyA8LSBzY2FuMSggZ2Vub3Byb2JzID0gc2hhcmVkLnByb2JzLAogICAgICAgICAgICAgICAgICAgICAgIHBoZW5vID0gYWxsX2RmX3NoYXJlZF9mYWN0b3JzX21hdF9yYW5rWiwKICAgICAgICAgICAgICAgICAgICAgICBraW5zaGlwID0gc2hhcmVkLmtpbnNoaXAsCiAgICAgICAgICAgICAgICAgICAgICAgYWRkY292YXIgPSBzaGFyZWQuY292YXIpCgojIGdldCBwZWFrcyBhYm92ZSBsb2QgNQphbGxfZGZfc2hhcmVkX3BlYWtzIDwtIGZpbmRfcGVha3MoIGFsbF9kZl9zY2FucywgbWFwID0gZ21hcCwgdGhyZXNob2xkID0gNSkKIyBpbnRlcnBvbGF0ZSBwaHlzaWNhbCBsb2NhdGlvbiBvZiBwZWFrcyAKYWxsX2RmX3NoYXJlZF9wZWFrcyA8LSBhbGxfZGZfc2hhcmVkX3BlYWtzICU+JQogIG11dGF0ZShwaGVub3R5cGU9bG9kY29sdW1uKSAlPiUKICBtdXRhdGUoIHBlYWtfY2hyID0gY2hyLAogICAgICAgICAgcGVha19jTSA9IHBvcywKICAgICAgICAgIGZhY3Rvcl9udW0gPSBhcy5udW1lcmljKHN0cl9zdWIobG9kY29sdW1uLCA3KSkgKSU+JQogIGludGVycF9icCguKSAjYWRkIGJwIGxvY2F0aW9uIGZvciBwZWFrcwoKIyBhZGQgbWFya2VycyBiZWZvcmUgJiBhZnRlciB0aGUgUVRMIHBlYWtzIGZvciBnZXR0aW5nIGVmZmVjdHMgbGF0ZXIKIyBHZXQgdGhlIGJvdW5kaW5nIG1hcmtlcnMgZm9yIGVhY2ggUVRMIHBlYWsKIyBpLmUuIG1hcmtlcnMgb24gdGhlIDY5ayBncmlkIHRoYXQgYXJlIHVwLSBhbmQgZG93bnN0cmVhbSBvZiB0aGUgcGVhawpxdWVyeSA8LSBhbGxfZGZfc2hhcmVkX3BlYWtzICU+JSAKICBkcGx5cjo6c2VsZWN0KHBlYWtfY2hyLCBpbnRlcnBfYnBfcGVhaykgJT4lCiAgZHBseXI6OnJlbmFtZShjaHJvbT1wZWFrX2Nociwgc3RhcnQ9aW50ZXJwX2JwX3BlYWspICU+JSBtdXRhdGUoZW5kPXN0YXJ0KSAlPiUKICBHZW5vbWljUmFuZ2VzOjpHUmFuZ2VzKCkKc3ViamVjdCA8LSBzZWxlY3QobWFwX2RhdDIsIGNocm9tLCBwb3NfYnApICU+JSAKICBkcGx5cjo6cmVuYW1lKHN0YXJ0PXBvc19icCkgJT4lCiAgbXV0YXRlKGVuZD1zdGFydCkgJT4lIAogIEdlbm9taWNSYW5nZXM6OkdSYW5nZXMoKSAgICMgbGVuZ3RoIDY5LDAwNQoKYWxsX2RmX3NoYXJlZF9wZWFrcyRiZWZvcmUgPC0gbWFwX2RhdDIkbWFya2VyW2ZvbGxvdyhxdWVyeSwgc3ViamVjdCldCmFsbF9kZl9zaGFyZWRfcGVha3MkYWZ0ZXIgPC0gbWFwX2RhdDIkbWFya2VyW3ByZWNlZGUocXVlcnksIHN1YmplY3QpXQoKIyBsb2FkaW5nIHRoZSBwZXJtdXRhdGlvbiByZXN1bHRzIGZvciBzaWduaWZpY2FuY2UgdGhyZXNob2xkcwpsb2FkKGhlcmUoIi4uL3BRVExfd2Vic2l0ZS9fZGF0YS9NT0ZBX2FsbF9kZl90aHJlc19yYW5rWi5SRGF0YSIpKQoKCiMgZ2V0IHRoZSBsaXN0IG9mIHNpZ25pZmljYW50IE1PRkEgUVRMCnN1bW1hcnkoYWxsX2RmX3NoYXJlZF90aHJlcykgJT4lCiAgYXNfdGliYmxlKCByb3duYW1lcyA9ICJTaWduaWZpY2FuY2UgbGV2ZWwiKSAlPiUKICBwaXZvdF9sb25nZXIobmFtZXNfdG8gPSAiRmFjdG9yIiwgdmFsdWVzX3RvPSJ0aHJlcyIsIGNvbHMgPSAyOjI0KSAlPiUKICBsZWZ0X2pvaW4oIGFsbF9kZl9zaGFyZWRfcGVha3MsIGJ5ID0gYygiRmFjdG9yIj0gInBoZW5vdHlwZSIpKSAlPiUKICBtdXRhdGUoIHBoZW5vdHlwZSA9IEZhY3RvcikgJT4lCiAgbXV0YXRlKCBsb2QgPSByb3VuZChsb2QsMSksIHRocmVzID0gcm91bmQodGhyZXMsIDEpKSAgJT4lCiAgZmlsdGVyKCBsb2QgPj0gdGhyZXMgICkgJT4lCiAgZmlsdGVyKCAhKGNociA9PSJYIiAmIGxvZCA8PSB0aHJlcys1KSApIC0+IGFsbF9kZl9zaGFyZWRfcGVha3Nfc2lnCgphbGxfZGZfc2hhcmVkX3BlYWtzX3NpZyAlPiUgCiAgbXV0YXRlKCBwZWFrX3Bvc19NYnAgPSByb3VuZCgoaW50ZXJwX2JwX3BlYWsvMWUwNiksMiksCiAgICAgICAgICBsb2QgPSByb3VuZChsb2QsMSksCiAgICAgICAgICBmYWN0b3JfbnVtID0gcGFzdGUoIkZhY3RvciIsIGZhY3Rvcl9udW0pLAogICAgICAgICAgcXRsX2xvYyA9IHBhc3RlMChjaHIsICI6ICIscGVha19wb3NfTWJwKSkgJT4lIAogIHNlbGVjdCggYE1PRkEgRmFjdG9yYCA9IGZhY3Rvcl9udW0sCiAgICAgICAgICBgUVRMIFBlYWsgbG9jYXRpb24gKENocjogTWJwKWAgPSBxdGxfbG9jLAogICAgICAgICAgYExPRCBzY29yZWAgPSBsb2QpICU+JSAKICBtdXRhdGUoCiAgICBUeXBlID0gY2FzZV93aGVuKCAKICAgICAgYE1PRkEgRmFjdG9yYCAlaW4lIGMoIkZhY3RvciAzIiwiRmFjdG9yIDEzIil+ImNhUVRMLCBlUVRMIiwKICAgICAgYE1PRkEgRmFjdG9yYCA9PSAiRmFjdG9yIDQifiJlUVRMIiwKICAgICAgYE1PRkEgRmFjdG9yYCAlaW4lIGMoIkZhY3RvciAxMiIsIkZhY3RvciAxNiIpfiJOZXciLAogICAgICBgTU9GQSBGYWN0b3JgID09ICJGYWN0b3IgMTQiICYgYFFUTCBQZWFrIGxvY2F0aW9uIChDaHI6IE1icClgPT0iMTY6IDY1LjU4In4iTmV3IiwKICAgICAgYE1PRkEgRmFjdG9yYCA9PSAiRmFjdG9yIDE0IiAmIGBRVEwgUGVhayBsb2NhdGlvbiAoQ2hyOiBNYnApYD09IjQ6IDE0Ny44OSJ+ImNhUVRMLCBlUVRMIiwKICAgICAgKQogICkgJT4lIAogIGNyZWF0ZV9kdCgpCgoKYGBgCgo8YnI+Cjxicj4KCiMjIyBGaWd1cmUgNUM6IE1PRkEgRmFjdG9yIDMgUVRMIG9uIGNocm9tb3NvbWUgMTUKCmBgYHtyIEZpZ3VyZV81Q19wcmVwLCBjYWNoZSA9IFRSVUV9CgojIGdldCBhbGxlbGUgZWZmZWN0cyBmb3IgTU9GQSBGYWN0b3IgMyBvbiBjaHIgMTUKZmFjdG9yM19jaHIxNV9xdGwgPC0gYWxsX2RmX3NoYXJlZF9wZWFrc19zaWcgJT4lCiAgZmlsdGVyKCBmYWN0b3JfbnVtID09MywgY2hyID09IDE1KQoKIyBydW4gZWZmZWN0cyBzY2FuIHVzaW5nIHF0bDI6OnNjYW4xYmx1cCAmIGdldCB0aGUgbWVhbiBvZiBhbGxlbGUgZWZmZWN0cyBiZXR3ZWVuIFFUTCBib3JkZXJzIChtYXJrZXJzIGJlZm9yZSAmIGFmdGVyKQpmYWN0b3IzX2NocjE1X3F0bF9lZmZzX3NjYW4gPC0gc2NhbjFibHVwKAogIGdlbm9wcm9icyA9c2hhcmVkLnByb2JzWywxNV0sCiAgcGhlbm8gPSBhbGxfZGZfc2hhcmVkX2ZhY3RvcnNfbWF0X3JhbmtaWywiRmFjdG9yMyIsIGRyb3AgPSBGQUxTRV0sCiAga2luc2hpcCA9IHNoYXJlZC5raW5zaGlwW1sxNV1dLAogIGFkZGNvdmFyID0gc2hhcmVkLmNvdmFyKQpmYWN0b3IzX2NocjE1X3F0bF9lZmZzIDwtIGNvbE1lYW5zKGZhY3RvcjNfY2hyMTVfcXRsX2VmZnNfc2NhbltjKGZhY3RvcjNfY2hyMTVfcXRsJGJlZm9yZSwgZmFjdG9yM19jaHIxNV9xdGwkYWZ0ZXIpLCBMRVRURVJTWzE6OF1dKSAKCiMgIyBnZXQgbG9kIHNjb3JlcyArIGVmZmVjdHMgZm9yIGFsbCB0aGUgcHJvdGVpbnMgYXQgdGhlIHNhbWUgbG9jdXMKcFFUTF9hdF9jaHIxNV9wZWFrIDwtIGMoKQptb2ZhX2ZhY3RvciA8LSAiRmFjdG9yMyIKbW9mYV9wZWFrX2NociA8LSBmYWN0b3IzX2NocjE1X3F0bCRjaHIKbW9mYV9tYXJrZXJzIDwtIGMoZmFjdG9yM19jaHIxNV9xdGwkYmVmb3JlLCBmYWN0b3IzX2NocjE1X3F0bCRhZnRlcikKcHJvYnMyX21hcmtlcnMgPC0gc3Vic2V0X3Byb2JzKCBwcm9icy5lc2NfcHJvdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzX2Nocm9tID1tb2ZhX3BlYWtfY2hyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXNfbWFya2VycyA9IG1vZmFfbWFya2VycyApCgpmb3IoIGkgaW4gMTpucm93KGFsbC5wcm90cykpewogICAgcHJvYnMyX21hcmtlcnMgPC0gc3Vic2V0X3Byb2JzKCBwcm9icy5lc2NfcHJvdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpc19jaHJvbSA9IG1vZmFfcGVha19jaHIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXNfbWFya2VycyA9IG1vZmFfbWFya2VycyApCiAgICBwUVRMX2VmZiA8LSBzY2FuMWJsdXAoZ2Vub3Byb2JzID0gcHJvYnMyX21hcmtlcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgcGhlbm8gPSBleHByWi5lc2NfcHJvdFssIGFsbC5wcm90cyRwcm90ZWluX2lkW2ldLGRyb3A9RkFMU0VdLAogICAgICAgICAgICAgICAgICAgICAgICAgIGtpbnNoaXBfbG9jby5lc2NfcHJvdFtbbW9mYV9wZWFrX2Nocl1dLAogICAgICAgICAgICAgICAgICAgICAgICAgIGFkZGNvdmFyID0gY292YXIuZXNjX3Byb3QKICAgICkKICAgIHBRVExfbWVhbl9lZmYgPC0gY29sTWVhbnMocFFUTF9lZmZbLExFVFRFUlNbMTo4XV0pCiAgICBuYW1lcyhwUVRMX21lYW5fZWZmKSA8LSBwYXN0ZTAoTEVUVEVSU1sxOjhdLCIuZXNjX3Byb3QiKQoKICAgIHBRVExfYXRfY2hyMTVfcGVha1tbaV1dIDwtIGMoIHByb3RlaW5faWQgPSBhbGwucHJvdHMkcHJvdGVpbl9pZFtpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBlYWtfY2hyID0gbW9mYV9wZWFrX2NociwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEZhY3RvciA9IG1vZmFfZmFjdG9yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcFFUTF9tZWFuX2VmZikKCiAgfQoKIyBwUVRMCiMgY29udmVydCB0byBtYXRyaXgKcFFUTF9hdF9jaHIxNV9wZWFrX2VmZnMgPC0gcFFUTF9hdF9jaHIxNV9wZWFrICU+JSAKICBkby5jYWxsKHJiaW5kLC4pICU+JSAKICBhc190aWJibGUoKSAlPiUgCiAgbXV0YXRlX2F0KC4sIGMocGFzdGUwKExFVFRFUlNbMTo4XSwiLmVzY19wcm90IikpLCBhcy5udW1lcmljICkKcFFUTF9hdF9jaHIxNV9wZWFrX2VmZl9tYXQgPC0gcFFUTF9hdF9jaHIxNV9wZWFrX2VmZnMlPiUgCiAgc2VsZWN0KGMocHJvdGVpbl9pZCxwYXN0ZTAoTEVUVEVSU1sxOjhdLCIuZXNjX3Byb3QiKSkgKSAlPiUgCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJwcm90ZWluX2lkIikgJT4lCiAgYXMubWF0cml4KCkgJT4lIAogIHQoKQpyb3duYW1lcyhwUVRMX2F0X2NocjE1X3BlYWtfZWZmX21hdCkgPC0gTEVUVEVSU1sxOjhdCmZhY3RvcjNfY2hyMTVfcXRsX2VmZnNfbWF0IDwtIGZhY3RvcjNfY2hyMTVfcXRsX2VmZnMgJT4lIAogIGFzLm1hdHJpeCgpCiMgZ2V0IGNvcnJlbGF0aW9ucwpmYWN0b3IzX3BxdGxfZWZmc19jb3JyIDwtIGNvcihwUVRMX2F0X2NocjE1X3BlYWtfZWZmX21hdCwgZmFjdG9yM19jaHIxNV9xdGxfZWZmc19tYXQpCmZhY3RvcjNfcHF0bF9lZmZzX3djb3JyIDwtIGZhY3RvcjNfcHF0bF9lZmZzX2NvcnIgJT4lIAogIGFzX3RpYmJsZSggcm93bmFtZXMgPSAicHJvdGVpbl9pZCIpICU+JSAKICByZW5hbWUoICJwcXRsX2NvciI9IFYxKSAKCiMgYWRkIHBRVEwgbG9kIGZvciBlYWNoIHByb3RlaW4KIyBnZXQgcFFUTCBsb2RzIGZyb20gc2NhbnMgKyBhZGQgaWRzCiMgbG9hZCBzY2Fucwpsb2FkKCIvcHJvamVjdHMvbXVuZ2VyLWxhYi9wcm9qZWN0cy9ET19tRVNDL3Byb3Rlb21pY3MvcHF0bF9tYXBwaW5nX1NBL0RPMTk1X21FU0NfcFFUTF9zY2Fuc19ub1BvbHlfdjIuUkRhdGEiKSAjIGVzYy5wcm90LnNjYW5zCmZhY3RvcjNfcHF0bF9sb2RzIDwtIGFwcGx5KCBlc2MucHJvdC5zY2Fuc1tjKGZhY3RvcjNfY2hyMTVfcXRsJGJlZm9yZSwgZmFjdG9yM19jaHIxNV9xdGwkYWZ0ZXIpLF0sMixtYXggKSAlPiUgCiAgYXNfdGliYmxlKCByb3duYW1lcyA9ICJwcm90ZWluX2lkIikgJT4lIAogIHJlbmFtZSggcFFUTF9sb2QgPSB2YWx1ZSkKcm0oZXNjLnByb3Quc2NhbnMpCgojIG1lcmdlIGFsbCBpbnRvIGEgZGF0YSBmcmFtZSBmb3IgcGxvdHRpbmcKRmlndXJlNUNfZGF0YSA8LSBmYWN0b3IzX3BxdGxfZWZmc193Y29yciAlPiUgCiAgbGVmdF9qb2luKCBmYWN0b3IzX3BxdGxfbG9kcykgJT4lIAogIGxlZnRfam9pbiggYWxsX2RmX3NoYXJlZF93ZWlnaHRzICU+JSAKICAgICAgICAgICAgICAgZmlsdGVyKCBmYWN0b3IgPT0gIkZhY3RvcjMiLCBmZWF0dXJlICVpbiUgYWxsLnByb3RzJHByb3RlaW5faWQpICU+JSAKICAgICAgICAgICAgICAgc2VsZWN0KCBwcm90ZWluX2lkID0gZmVhdHVyZSwgcHJvdGVpbl93ZWlnaHQgPSB2YWx1ZSkKICApICU+JQogICMgYWRkIHByb3RlaW4gZGV0YWlscwogIGxlZnRfam9pbiggLiwgYWxsLnByb3RzMiAlPiUgCiAgICAgICAgICAgICAgIHNlbGVjdCggcHJvdGVpbl9pZCwgZ2VuZV9jaHIsIG1pZHBvaW50LCBtZ2lfc3ltYm9sKSkgJT4lIAogICMgYWRkIG1vZmEgcXRsIGRldGFpbHMKICBjYmluZCggLiwgZmFjdG9yM19jaHIxNV9xdGwgJT4lIAogICAgICAgICAgIHNlbGVjdCggZmFjdG9yM19xdGxfcG9zID1pbnRlcnBfYnBfcGVhayAsIGZhY3RvcjNfcXRsX3BlYWtfY2hyID0gcGVha19jaHIpCiAgICAgICAgICkgJT4lIAogIGZpbHRlcighaXMubmEocHF0bF9jb3IpKSAlPiUgCiAgZmlsdGVyKCAgIShmYWN0b3IzX3F0bF9wZWFrX2NociA9PSBnZW5lX2NociAmCiAgICAgICAgICAgICAgYWJzKCBtaWRwb2ludCAtIGFzLm51bWVyaWMoZmFjdG9yM19xdGxfcG9zKSkgPCAxMGUwNiApICkgJT4lIAogIHNlbGVjdCgKICAgIGBQcm90ZWluIElEYCA9IHByb3RlaW5faWQsCiAgICBgTUdJIHN5bWJvbGAgPSBtZ2lfc3ltYm9sLAogICAgYENvcnJlbGF0aW9uIGJldHdlZW4gUVRMIGVmZmVjdHNgID0gcHF0bF9jb3IsCiAgICBgTE9EIHNjb3JlYCA9IHBRVExfbG9kLAogICAgYFByb3RlaW4gd2VpZ2h0YCA9IHByb3RlaW5fd2VpZ2h0CiAgKQoKYGBgCgo8YnI+CgpgYGB7ciBGaWd1cmU1Y19wbG90LCAgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9NSwgZmlnLmNhcCA9ICJGaWd1cmUgNUM6IEZvciBhbGwgZXhwcmVzc2VkIHByb3RlaW5zLCB0aGUgcFFUTCBMT0Qgc2NvcmUgY2FsY3VsYXRlZCBhdCB0aGUgQ2hyIDE1IFFUTCBwZWFrIGlzIHBsb3R0ZWQgb24gdGhlIHktYXhpcyByZWxhdGl2ZSB0byB0aGUgcHJvdGVpbuKAmXMgY29udHJpYnV0aW9uIChmYWN0b3Igd2VpZ2h0KSB0byBNT0ZBIEZhY3RvciAzIG9uIHRoZSB4LWF4aXMuIFByb3RlaW5zIHdpdGggYWJzb2x1dGUgZmFjdG9yIHdlaWdodHMgbGVzcyB0aGFuIDAuMSB3ZXJlIGZpbHRlcmVkLiBDb3JyZWxhdGlvbiBiZXR3ZWVuIGFsbGVsZSBlZmZlY3RzIGF0IHRoZSBDaHIgMTUgcFFUTCBmb3IgaW5kaXZpZHVhbCBwcm90ZWlucyB0byBhbGxlbGUgZWZmZWN0cyBvZiB0aGUgRmFjdG9yIDMgUVRMLiBJbmRpdmlkdWFsIGdlbmVzIHRoYXQgbWFwcGVkIHdpdGggYSBzaWduaWZpY2FudCBRVEwgKExPRCA+IDcuNSkgYXJlIGNvbG9yZWQgZ3JheSwgYW5kIGhpZ2hsaWdodCB0aGF0IG1hbnkgcHJvdGVpbnMgY29udHJpYnV0ZSBzdWJzdGFudGlhbGx5IHRvIEZhY3RvciAzIGFuZCBzaG93IGhpZ2ggYWdyZWVtZW50IGluIGFsbGVsZSBlZmZlY3RzIGF0IHRoZSBDaHIgMTUgUVRMIChkYXJrIHJlZCBhbmQgYmx1ZSksIGRlc3BpdGUgbm90IG1hcHBpbmcgaW5kaXZpZHVhbGx5IHdpdGggYSBzaWduaWZpY2FudCBRVEwgYXQgdGhhdCBsb2N1cy4ifQoKRmlndXJlNUNfZGF0YSAlPiUgCiAgZmlsdGVyKCBgTE9EIHNjb3JlYCA8IDcuNSwgCiAgICAgICAgICBhYnMoYFByb3RlaW4gd2VpZ2h0YCkgPjAuMSApICU+JSAKICBnZ3Bsb3QoKSsKICBhZXMoCiAgICB4ID0gYFByb3RlaW4gd2VpZ2h0YCwKICAgIHkgPSAgYExPRCBzY29yZWAsCiAgICBjb2wgPSBgQ29ycmVsYXRpb24gYmV0d2VlbiBRVEwgZWZmZWN0c2AsCiAgICB0ZXh0ID0gcGFzdGUoIkdlbmU6ICIsIGBNR0kgc3ltYm9sYCkgCiAgKSsKICBnZW9tX3BvaW50KHNpemUgPSAzLCBhbHBoYSA9IDAuNikrCiAgdGhlbWVfcHViY2xlYW4oYmFzZV9zaXplID0gMTgpKwogIHNjYWxlX2NvbG9yX2dyYWRpZW50MiggbGltaXRzID0gYygtMSwxKSkrCiAgeGxhYigiUHJvdGVpbiB3ZWlnaHQiKSsKICB5bGFiKCJMT0Qgc2NvcmUiKSsKICBsYWJzKCBjb2wgPSAiQ29ycmVsYXRpb24gYmV0d2VlblxuUVRMIGVmZmVjdHMiKSArCiAgeGxpbSgtMC43NSwwLjQpKwogIHlsaW0oMCwxNSkrCiAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoYW5nbGUgPSAzMCksIAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dCh2anVzdCA9IDEuMykpKwogICMgYWRkIHRoZSBzaWduaWZpbmNhdG4gcXRsIGluIGdyYXkKICBnZW9tX3BvaW50KCBkYXRhID0gZmlsdGVyKEZpZ3VyZTVDX2RhdGEsIGBMT0Qgc2NvcmVgID4gNy41LCBhYnMoYFByb3RlaW4gd2VpZ2h0YCk+MC4xKSwKICAgICAgICAgICAgICBjb2wgPSAiZ3JheSIsIHNpemUgPTMsIGFscGhhID0gMC42KS0+IEZpZ3VyZTVDCgojZ2dwbG90bHkoRmlndXJlNUMsICB3aWR0aCA9IDcwMCwgaGVpZ2h0ID0gNDAwKQogCkZpZ3VyZTVDIAoKYGBgCgo8YnI+CgpgYGB7ciBkYXRhX2Zvcl9maWd1cmU1YywgZmlnLmNhcCA9ICJEYXRhIHBsb3R0ZWQgaW4gRmlndXJlIDVDLiJ9CgpGaWd1cmU1Q19kYXRhICU+JSAKICBtdXRhdGVfaWYoIGlzLm51bWVyaWMsIHJvdW5kLCAyKSAlPiUgCiAgY3JlYXRlX2R0KCkKCmBgYAoKPGJyPgo8YnI+CgojIyMgRmlndXJlIDVEOiBNT0ZBIEZhY3RvciA0IFFUTCBvbiBjaHJvbW9zb21lIDEwIAoKCmBgYHtyIEZpZ3VyZV81RF9wcmVwLCBjYWNoZSA9IFRSVUV9CgojIGdldCBhbGxlbGUgZWZmZWN0cyBmb3IgTU9GQSBGYWN0b3IgNCBvbiBjaHIgMTAKZmFjdG9yNF9jaHIxMF9xdGwgPC0gYWxsX2RmX3NoYXJlZF9wZWFrc19zaWcgJT4lCiAgZmlsdGVyKCBmYWN0b3JfbnVtID09IDQsIGNociA9PSAxMCkKCiMgcnVuIGVmZmVjdHMgc2NhbiB1c2luZyBxdGwyOjpzY2FuMWJsdXAgJiBnZXQgdGhlIG1lYW4gb2YgYWxsZWxlIGVmZmVjdHMgYmV0d2VlbiBRVEwgYm9yZGVycyAobWFya2VycyBiZWZvcmUgJiBhZnRlcikKZmFjdG9yNF9jaHIxMF9xdGxfZWZmc19zY2FuIDwtIHNjYW4xYmx1cCgKICBnZW5vcHJvYnMgPXNoYXJlZC5wcm9ic1ssMTBdLAogIHBoZW5vID0gYWxsX2RmX3NoYXJlZF9mYWN0b3JzX21hdF9yYW5rWlssIkZhY3RvcjQiLCBkcm9wID0gRkFMU0VdLAogIGtpbnNoaXAgPSBzaGFyZWQua2luc2hpcFtbMTBdXSwKICBhZGRjb3ZhciA9IHNoYXJlZC5jb3ZhcikKZmFjdG9yNF9jaHIxMF9xdGxfZWZmcyA8LSBjb2xNZWFucyhmYWN0b3I0X2NocjEwX3F0bF9lZmZzX3NjYW5bYyhmYWN0b3I0X2NocjEwX3F0bCRiZWZvcmUsIGZhY3RvcjRfY2hyMTBfcXRsJGFmdGVyKSwgTEVUVEVSU1sxOjhdXSkKCiMgZ2V0IGxvZCBzY29yZXMgKyBlZmZlY3RzIGZvciBhbGwgdGhlIHByb3RlaW5zIGF0IHRoZSBzYW1lIGxvY3VzCmVRVExfYXRfY2hyMTBfcGVhayA8LSBjKCkKbW9mYV9mYWN0b3IgPC0gIkZhY3RvcjQiCm1vZmFfcGVha19jaHIgPC0gZmFjdG9yNF9jaHIxMF9xdGwkY2hyICAKbW9mYV9tYXJrZXJzIDwtIGMoZmFjdG9yNF9jaHIxMF9xdGwkYmVmb3JlLCBmYWN0b3I0X2NocjEwX3F0bCRhZnRlcikKcHJvYnMyX21hcmtlcnMgPC0gc3Vic2V0X3Byb2JzKCBwcm9icy5lc2Nfcm5hLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzX2Nocm9tID1tb2ZhX3BlYWtfY2hyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzX21hcmtlcnMgPSBtb2ZhX21hcmtlcnMgKQoKZm9yKCBpIGluIDE6bnJvdyhhbGwuZ2VuZXMpKXsKICAgIHByb2JzMl9tYXJrZXJzIDwtIHN1YnNldF9wcm9icyggcHJvYnMuZXNjX3JuYSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXNfY2hyb20gPSBtb2ZhX3BlYWtfY2hyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpc19tYXJrZXJzID0gbW9mYV9tYXJrZXJzICkKICAKICAgIGVRVExfZWZmIDwtIHNjYW4xYmx1cChnZW5vcHJvYnMgPSBwcm9iczJfbWFya2VycywKICAgICAgICAgICAgICAgICAgICAgICAgICBwaGVubyA9IGV4cHJaLmVzY19ybmFbLCBhbGwuZ2VuZXMkZW5zZW1ibF9nZW5lX2lkW2ldLGRyb3A9RkFMU0VdLAogICAgICAgICAgICAgICAgICAgICAgICAgIGtpbnNoaXBfbG9jby5lc2Nfcm5hW1ttb2ZhX3BlYWtfY2hyXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgYWRkY292YXIgPSBjb3Zhci5lc2Nfcm5hCiAgICApCiAgICBlUVRMX21lYW5fZWZmIDwtIGNvbE1lYW5zKGVRVExfZWZmWyxMRVRURVJTWzE6OF1dKQogICAgbmFtZXMoZVFUTF9tZWFuX2VmZikgPC0gcGFzdGUwKExFVFRFUlNbMTo4XSwiLmVzY19ybmEiKQogICAgCiAgICBlUVRMX2F0X2NocjEwX3BlYWtbW2ldXSA8LSBjKCBlbnNlbWJsX2dlbmVfaWQgPSBhbGwuZ2VuZXMkZW5zZW1ibF9nZW5lX2lkW2ldLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGVha19jaHIgPSBtb2ZhX3BlYWtfY2hyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRmFjdG9yID0gbW9mYV9mYWN0b3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlUVRMX21lYW5fZWZmKQogICAgCiAgfQoKIyBlUVRMCiMgY29udmVydCB0byBtYXRyaXgKZVFUTF9hdF9jaHIxMF9wZWFrX2VmZnMgPC0gZVFUTF9hdF9jaHIxMF9wZWFrICU+JSAKICBkby5jYWxsKHJiaW5kLC4pICU+JSAKICBhc190aWJibGUoKSAlPiUgCiAgbXV0YXRlX2F0KC4sIGMocGFzdGUwKExFVFRFUlNbMTo4XSwiLmVzY19ybmEiKSksIGFzLm51bWVyaWMgKQplUVRMX2F0X2NocjEwX3BlYWtfZWZmc19tYXQgPC0gZVFUTF9hdF9jaHIxMF9wZWFrX2VmZnMlPiUgCiAgc2VsZWN0KGMoZW5zZW1ibF9nZW5lX2lkLHBhc3RlMChMRVRURVJTWzE6OF0sIi5lc2Nfcm5hIikpICkgJT4lIAogIGNvbHVtbl90b19yb3duYW1lcygiZW5zZW1ibF9nZW5lX2lkIikgJT4lCiAgYXMubWF0cml4KCkgJT4lIAogIHQoKQpyb3duYW1lcyhlUVRMX2F0X2NocjEwX3BlYWtfZWZmc19tYXQpIDwtIExFVFRFUlNbMTo4XQpmYWN0b3I0X2NocjEwX3F0bF9lZmZzX21hdCA8LSBmYWN0b3I0X2NocjEwX3F0bF9lZmZzICU+JSAKICBhcy5tYXRyaXgoKQojIGdldCBjb3JyZWxhdGlvbnMKZmFjdG9yNF9lcXRsX2VmZnNfY29yciA8LSBjb3IoZVFUTF9hdF9jaHIxMF9wZWFrX2VmZnNfbWF0LCBmYWN0b3I0X2NocjEwX3F0bF9lZmZzX21hdCkKZmFjdG9yNF9lcXRsX2VmZnNfd2NvcnIgPC0gZmFjdG9yNF9lcXRsX2VmZnNfY29yciAlPiUgCiAgYXNfdGliYmxlKCByb3duYW1lcyA9ICJlbnNlbWJsX2dlbmVfaWQiKSAlPiUgCiAgcmVuYW1lKCAiZXF0bF9jb3IiPSBWMSkgCgojIGFkZCBlUVRMIGxvZAojIGdldCBlUVRMIGxvZHMgZnJvbSBzY2FucyArIGFkZCBpZHMKIyBsb2FkIHNjYW5zCmxvYWQoIi9wcm9qZWN0cy9tdW5nZXItbGFiL3Byb2plY3RzL0RPX21FU0Mvcm5hX3NlcS9xdGxfbWFwcGluZy90b3RhbF9nZW5lX2V4cHJlc3Npb24vZXF0bF9ncmlkNjlrX3BlL0RPMTg1X21FU0NfcGFpcmVkX2VRVExfc2NhbnMuUkRhdGEiKSAjIGVzYy5ybmEuc2NhbnMKZmFjdG9yNF9lcXRsX2xvZHMgPC0gYXBwbHkoIGVzYy5ybmEuc2NhbnNbYyhmYWN0b3I0X2NocjEwX3F0bCRiZWZvcmUsIGZhY3RvcjRfY2hyMTBfcXRsJGFmdGVyKSxdLDIsbWF4ICkgJT4lIAogIGFzX3RpYmJsZSggcm93bmFtZXMgPSAiZW5zZW1ibF9nZW5lX2lkIikgJT4lIAogIHJlbmFtZSggZVFUTF9sb2QgPSB2YWx1ZSkKcm0oZXNjLnJuYS5zY2FucykKCiMgbWVyZ2UgYWxsIGludG8gYSBkYXRhIGZyYW1lCkZpZ3VyZTVEX2RhdGEgPC0gZmFjdG9yNF9lcXRsX2VmZnNfd2NvcnIgJT4lIAogIGxlZnRfam9pbiggZmFjdG9yNF9lcXRsX2xvZHMpICU+JSAKICBsZWZ0X2pvaW4oIGFsbF9kZl9zaGFyZWRfd2VpZ2h0cyAlPiUgCiAgICAgICAgICAgICAgIGZpbHRlciggZmFjdG9yID09ICJGYWN0b3I0IiwgZmVhdHVyZSAlaW4lIGFsbC5nZW5lcyRlbnNlbWJsX2dlbmVfaWQpICU+JSAKICAgICAgICAgICAgICAgc2VsZWN0KCBlbnNlbWJsX2dlbmVfaWQgPSBmZWF0dXJlLCB0cmFuc2NyaXB0X3dlaWdodCA9IHZhbHVlKQogICkgJT4lIAogICMgYWRkIGdlbmUgZGV0YWlscwogIGxlZnRfam9pbiggLiwgYWxsLmdlbmVzMiAlPiUgCiAgICAgICAgICAgICAgIHNlbGVjdCggZW5zZW1ibF9nZW5lX2lkLCBtaWRwb2ludCwgZ2VuZV9jaHIsIG1naV9zeW1ib2wpKSAlPiUgCiAgIyBhZGQgbW9mYSBxdGwgZGV0YWlscwogIGNiaW5kKC4sIGZhY3RvcjRfY2hyMTBfcXRsICU+JSAKICAgICAgICAgIHNlbGVjdCggZmFjdG9yNF9xdGxfcG9zID1pbnRlcnBfYnBfcGVhayAsIGZhY3RvcjRfcXRsX3BlYWtfY2hyID0gcGVha19jaHIpCiAgKSAlPiUgCiAgZmlsdGVyKCFpcy5uYShlcXRsX2NvcikpICU+JSAKICBmaWx0ZXIoICAhKGZhY3RvcjRfcXRsX3BlYWtfY2hyID09IGdlbmVfY2hyICYKICAgICAgICAgICAgICBhYnMoIG1pZHBvaW50IC0gYXMubnVtZXJpYyhmYWN0b3I0X3F0bF9wb3MpKSA8IDEwZTA2ICkgKSAlPiUgCiAgc2VsZWN0KAogICAgYEdlbmUgSURgID0gZW5zZW1ibF9nZW5lX2lkLAogICAgYE1HSSBzeW1ib2xgID0gbWdpX3N5bWJvbCwKICAgIGBDb3JyZWxhdGlvbiBiZXR3ZWVuIFFUTCBlZmZlY3RzYCA9IGVxdGxfY29yLAogICAgYExPRCBzY29yZWAgPSBlUVRMX2xvZCwKICAgIGBUcmFuc2NyaXB0IHdlaWdodGAgPSB0cmFuc2NyaXB0X3dlaWdodAogICkKCgpgYGAKCjxicj4KCgpgYGB7ciBGaWd1cmVfNURfcGxvdCwgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9NSwgZmlnLmNhcCA9ICJGaWd1cmUgNUQ6IEZvciBhbGwgZXhwcmVzc2VkIHRyYW5zY3JpcHRzLCB0aGUgZVFUTCBMT0Qgc2NvcmUgYXQgdGhlIENociAxMCBRVEwgcGVhayBpcyBwbG90dGVkIG9uIHRoZSB5LWF4aXMgcmVsYXRpdmUgdG8gdGhhdCB0cmFuc2NyaXB04oCZcyBjb250cmlidXRpb24gdG8gRmFjdG9yIDQgb24gdGhlIHgtYXhpcy4gQWdhaW4sIHRyYW5zY3JpcHRzIHdpdGggYWJzb2x1dGUgZmFjdG9yIHdlaWdodHMgbGVzcyB0aGFuIDAuMSB3ZXJlIGZpbHRlcmVkLCBhbmQgaW5kaXZpZHVhbCBwb2ludHMgYXJlIGFzIGRlc2NyaWJlZCBpbiBwYW5lbCBDLiBNYW55IHRyYW5zY3JpcHRzIGNvbnRyaWJ1dGUgdG8gRmFjdG9yIDQgYW5kIGhhdmUgY29ycmVsYXRlZCBhbGxlbGUgZWZmZWN0cyBhdCB0aGUgQ2hyIDEwIFFUTCwgZGVzcGl0ZSBpbmRpdmlkdWFsbHkgZmFpbGluZyB0byBtYXAgd2l0aCBhIHNpZ25pZmljYW50IENociAxMCBlUVRMLiJ9CgoKRmlndXJlNURfZGF0YSAlPiUgCiAgZmlsdGVyKCBgTE9EIHNjb3JlYCA8IDcuNSwgCiAgICAgICAgICBhYnMoYFRyYW5zY3JpcHQgd2VpZ2h0YCkgPjAuMSApICU+JSAKICBnZ3Bsb3QoKSsKICBhZXMoCiAgICB4ID0gYFRyYW5zY3JpcHQgd2VpZ2h0YCwKICAgIHkgPSAgYExPRCBzY29yZWAsCiAgICBjb2wgPSBgQ29ycmVsYXRpb24gYmV0d2VlbiBRVEwgZWZmZWN0c2AsCiAgICB0ZXh0ID0gcGFzdGUoIkdlbmU6ICIsIGBNR0kgc3ltYm9sYCkgCiAgKSsKICBnZW9tX3BvaW50KHNpemUgPSAzLCBhbHBoYSA9IDAuNikrCiAgdGhlbWVfcHViY2xlYW4oYmFzZV9zaXplID0gMTgpKwogIHNjYWxlX2NvbG9yX2dyYWRpZW50MiggbGltaXRzID0gYygtMSwxKSkrCiAgeGxhYigiVHJhbnNjcmlwdCB3ZWlnaHQiKSsKICB5bGFiKCJMT0Qgc2NvcmUiKSsKICBsYWJzKCBjb2wgPSAiQ29ycmVsYXRpb24gYmV0d2VlblxuUVRMIGVmZmVjdHMiKSArCiAgeWxpbSgwLDE1KSsKICB4bGltKC0wLjUsIDEpKwogIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMzApLCAKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAidG9wIiwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQodmp1c3QgPSAxLjMpKSsKICAjIGFkZCB0aGUgc2lnbmlmaWNhbnQgcXRsIGluIGdyYXkKICBnZW9tX3BvaW50KCBkYXRhID0gZmlsdGVyKEZpZ3VyZTVEX2RhdGEsIGBMT0Qgc2NvcmVgID4gNy41LCBhYnMoYFRyYW5zY3JpcHQgd2VpZ2h0YCk+MC4xKSwKICAgICAgICAgICAgICBjb2wgPSAiZ3JheSIsIHNpemUgPTMsIGFscGhhID0gMC42KSAtPiBGaWd1cmU1RAoKI2dncGxvdGx5KEZpZ3VyZTVELCB3aWR0aCA9IDcwMCwgaGVpZ2h0ID0gNDAwKQpGaWd1cmU1RAoKYGBgCgo8YnI+CgpgYGB7ciBkYXRhX2Zvcl9maWd1cmU1ZCwgZmlnLmNhcCA9ICJEYXRhIHBsb3R0ZWQgaW4gRmlndXJlIDVELiJ9CgpGaWd1cmU1RF9kYXRhICU+JSAKICBtdXRhdGVfaWYoIGlzLm51bWVyaWMsIHJvdW5kLCAyKSAlPiUgCiAgY3JlYXRlX2R0KCkKCmBgYAoKPGJyPgo8YnI+CgoKCiMjIyBGaWd1cmUgNUU6IE1lZGlhdGlvbiBvZiBGYWN0b3IgNCBRVEwgb24gY2hyb21vc29tZSAxMAoKYGBge3IgRmlndXJlXzVFX3ByZXB9CgojIHByZXAgZmFjdG9yIDQgcXRsIGZvciBtZWRpYXRpb24KZmFjdG9yNF9jaHIxMF9xdGwgPC0gZmFjdG9yNF9jaHIxMF9xdGwgJT4lIAogIG11dGF0ZShwb3NfY00gPSBhcy5kb3VibGUocG9zKSkgJT4lCiAgbGVmdF9qb2luKHNlbGVjdChtYXBfZGF0MiwgLWNociksIGJ5ID0gYygicG9zX2NNIikpICU+JQogIGZpbHRlcighaXMubmEobWFya2VyKSkgJT4lCiAgbXV0YXRlKHBoZW5vdHlwZSA9IGxvZGNvbHVtbikKCiMgZmFjdG9yIDQgcXRsIHNjYW4Kc2NhbjFfZmFjdG9yNCA8LSBzY2FuMShnZW5vcHJvYnMgPSBzaGFyZWQucHJvYnMsCiAgICAgICAgICAgICAgICAgcGhlbm8gPSAgYWxsX2RmX3NoYXJlZF9mYWN0b3JzX21hdF9yYW5rWlssIkZhY3RvcjQiLGRyb3AgPSBGQUxTRV0sCiAgICAgICAgICAgICAgICAga2luc2hpcCA9IHNoYXJlZC5raW5zaGlwLAogICAgICAgICAgICAgICAgIGFkZGNvdmFyID0gc2hhcmVkLmNvdmFyKQoKIyBmYWN0b3IgNCBtZWRpYXRpb24KIyBwaGVub3R5cGUgdG8gYmUgbWVkaWF0ZWQgIAp0YXJnZXQgPC0gYWxsX2RmX3NoYXJlZF9mYWN0b3JzX21hdF9yYW5rWlssICJGYWN0b3I0IiwgZHJvcCA9IEZBTFNFXQojIHByZXAgRVNDIHRyYW5zY3JpcHQgbWVkaWF0b3JzICYgYW5ub3RhdGlvbnMKbWVkcy5ybmEgPC0gdGliYmxlKGVuc2VtYmxfZ2VuZV9pZCA9IGNvbG5hbWVzKGV4cHIuZXNjX3JuYSkpICU+JQogIGxlZnRfam9pbihhbGwuZ2VuZXMpICU+JQogIG11dGF0ZSggaWQgPSBlbnNlbWJsX2dlbmVfaWQpICU+JQogIG11dGF0ZShjaHJvbSA9IGlmZWxzZShnZW5lX2NociA9PSAiTVQiLCAiTSIsIGdlbmVfY2hyKSkgJT4lCiAgZmlsdGVyKCFpcy5uYShjaHJvbSkpCm1lZGlhdG9yLnJuYSA8LSBleHByWi5lc2Nfcm5hW3RocmVld2F5LnNoYXJlZC5zYW1wbGVzJHNhbXBsZWlkLAogIG1lZHMucm5hJGVuc2VtYmxfZ2VuZV9pZCwKICBkcm9wID0gRkFMU0UKXSAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgcm93bmFtZXNfdG9fY29sdW1uKCkgJT4lCiAgbGVmdF9qb2luKC4sIHNlbGVjdCh0aHJlZXdheS5zaGFyZWQuc2FtcGxlcywgc2FtcGxlaWQsIHRvcF9tdWdhKSwgYnkgPSBjKCJyb3duYW1lIiA9ICJzYW1wbGVpZCIpKSAlPiUKICBjb2x1bW5fdG9fcm93bmFtZXMoInRvcF9tdWdhIikgJT4lCiAgc2VsZWN0KC1yb3duYW1lKSAlPiUKICBhcy5tYXRyaXgoKQojIHByZXAgRVNDIHByb3RlaW4gbWVkaWF0b3JzICYgYW5ub3RhdGlvbnMKbWVkcy5wcm90IDwtIHRpYmJsZShwcm90ZWluX2lkID0gY29sbmFtZXMoZXhwclouZXNjX3Byb3RbLGFsbC5wcm90cyRwcm90ZWluX2lkXSkpICU+JQogIGxlZnRfam9pbihhbGwucHJvdHMpICU+JQogIG11dGF0ZSggaWQgPSBwcm90ZWluX2lkKSAlPiUKICBtdXRhdGUoY2hyb20gPSBpZmVsc2UoZ2VuZV9jaHIgPT0gIk1UIiwgIk0iLCBnZW5lX2NocikpICU+JQogIG11dGF0ZShlbmQgPSBnZW5lX2VuZCwgc3RhcnQgPSBnZW5lX3N0YXJ0LCBpZCA9IHByb3RlaW5faWQpCm1lZGlhdG9yLnByb3QgPC0gZXhwclouZXNjX3Byb3RbdGhyZWV3YXkuc2hhcmVkLnNhbXBsZXMkc2FtcGxlaWQsCiAgbWVkcy5wcm90JHByb3RlaW5faWQsCiAgZHJvcCA9IEZBTFNFCl0gJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbigpICU+JQogIGxlZnRfam9pbiguLCBzZWxlY3QodGhyZWV3YXkuc2hhcmVkLnNhbXBsZXMsIHNhbXBsZWlkLCB0b3BfbXVnYSksIGJ5ID0gYygicm93bmFtZSIgPSAic2FtcGxlaWQiKSkgJT4lCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJ0b3BfbXVnYSIpICU+JQogIHNlbGVjdCgtcm93bmFtZSkgJT4lCiAgYXMubWF0cml4KCkKIyBydW4gbWVkaWF0aW9uIHVzaW5nIGEgd3JhcHBlciBmdW5jdGlvbgpnZXRfbWVkcyA8LSBmdW5jdGlvbih0YXJnZXQsIG1lZHMsIG1lZGlhdG9yLCBwZWFrcywgcHJvYnMsY292YXIsIHpfdGhyZXMgPSAtNCwgIHBvc190aHJlcyA9IDEwICl7CiAgbWVkLnNjYW4uYWxsIDwtIGMoKQogIHNhbXBsZXMgPC0gcm93bmFtZXMobWVkaWF0b3IpCiAgZm9yKCBpIGluIDE6bnJvdyhwZWFrcykpewogICAgI3ByaW50KCBwYXN0ZTAoImkgaXMgIiwgaSkpCiAgICAKICAgIG1hcmtlciAgICA8LSBtYXBfZGF0MiAlPiUgZmlsdGVyKHBvc19jTSA9PSBwZWFrcyRwb3NfY01baV0pCiAgICBhbm5vdCAgICAgPC0gbWVkcyAlPiUgbXV0YXRlKGNocj1nZW5lX2Nocixwb3M9YWJzKGdlbmVfZW5kK2dlbmVfc3RhcnQpLzIpCiAgICBnZW5vICAgICAgPC0gcHVsbF9nZW5vcHJvYnBvcyhwcm9icyxtYXJrZXIkbWFya2VyKQogICAgZ2VubyAgICAgIDwtIGdlbm9bc2FtcGxlcyxdCiAgICAKICAgIGlmKCAhaXMubnVsbChjb3ZhcikpeyAKICAgICAgY292YXIgPC0gY292YXJbc2FtcGxlcywsZHJvcD1GQUxTRV0gCiAgICB9CiAgICAKICAgIG1lZC5zY2FuIDwtIG1lZGlhdGlvbi5zY2FuKHRhcmdldD0gdGFyZ2V0W3NhbXBsZXMscGVha3MkcGhlbm90eXBlW2ldLCBkcm9wPUZBTFNFXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lZGlhdG9yID0gbWVkaWF0b3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uID0gYW5ub3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb3ZhciA9ICBjb3ZhciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF0bC5nZW5vID0gZ2VubywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCAgICAgPSAiZG91YmxlLWxvZC1kaWZmIikgCiAgICAKICAgIG1lZC5zY2FuIDwtIG1lZC5zY2FuICU+JSAKICAgICAgbXV0YXRlKHBoZW5vdHlwZT0gcGVha3MkcGhlbm90eXBlW2ldLCAKICAgICAgICAgICAgIHBlYWtfY2hyID0gcGVha3MkcGVha19jaHJbaV0sIAogICAgICAgICAgICAgcGVha19sb2QgPSBwZWFrcyRsb2RbaV0sCiAgICAgICAgICAgICBwZWFrX3Bvc19NYnAgPSBwZWFrcyRwb3NfYnAvMWUwNiwKICAgICAgICAgICAgIG1lZF9jaHIgID0gY2hyKSAlPiUgCiAgICAgIG11dGF0ZShzY2FsZWRfTE9EID0gc2NhbGUoTE9EKSwgCiAgICAgICAgICAgICBtaWRkbGUgPSAoZ2VuZV9lbmQrZ2VuZV9zdGFydCkvMmUwNikgJT4lCiAgICAgIGZpbHRlciggKCNzY2FsZWRfTE9EIDwgel90aHJlcyAmIAogICAgICAgICAgICAgICAgIHBlYWtfY2hyID09ICBtZWRfY2hyICYgCiAgICAgICAgICAgICAgICAgYWJzKG1pZGRsZSAtIHBlYWtfcG9zX01icCkgPD0gcG9zX3RocmVzKSAmCiAgICAgICAgICAgICAgICAgKExPRCA8IHBlYWtfbG9kKSkKICAgIAogICAgbWVkLnNjYW4uYWxsIDwtIHJiaW5kKG1lZC5zY2FuLmFsbCxtZWQuc2NhbikKICB9CiAgcmV0dXJuKG1lZC5zY2FuLmFsbCkKfQpmYWN0b3I0X3JuYV9tZWRzIDwtICBnZXRfbWVkcygKICB0YXJnZXQgPSB0YXJnZXQsCiAgbWVkcyA9IG1lZHMucm5hLAogIG1lZGlhdG9yID0gbWVkaWF0b3Iucm5hLAogIHBlYWtzID0gZmFjdG9yNF9jaHIxMF9xdGwsCiAgcHJvYnMgPSBzaGFyZWQucHJvYnMsCiAgY292YXIgPSBzaGFyZWQuY292YXIKKQpmYWN0b3I0X3Byb3RfbWVkcyA8LSAgZ2V0X21lZHMoCiAgdGFyZ2V0ID0gdGFyZ2V0LAogIG1lZHMgPSBtZWRzLnByb3QsCiAgbWVkaWF0b3IgPSBtZWRpYXRvci5wcm90LAogIHBlYWtzID0gZmFjdG9yNF9jaHIxMF9xdGwsCiAgcHJvYnMgPSBzaGFyZWQucHJvYnMsCiAgY292YXIgPSBzaGFyZWQuY292YXIKKQoKZmFjdG9yNF9tZWRzIDwtIGZhY3RvcjRfcm5hX21lZHMgJT4lIAogIG11dGF0ZSggdHlwZSA9ICJSTkEiKSAlPiUgCiAgcmJpbmQoIGZhY3RvcjRfcHJvdF9tZWRzICU+JSAKICAgICAgICAgICBtdXRhdGUoIHR5cGUgPSAicHJvdGVpbiIpKSAlPiUgCiAgbXV0YXRlKAogICAgYFBoZW5vdHlwZWAgPSBwaGVub3R5cGUsIAogICAgYE1lZGlhdG9yIE1HSSBzeW1ib2xgID0gbWdpX3N5bWJvbCwKICAgIGBRVEwgY2hyb21vc29tZWAgPSBwZWFrX2NociwgCiAgICBgTWVkaWF0b3IgY2hyb21vc29tZWAgPSBjaHIsCiAgICBgUVRMIExPRGAgPSBwZWFrX2xvZCwKICAgIGBNZWRpYXRlZCBMT0RgID0gTE9ELAogICAgYFFUTCBwb3NpdGlvbiAoTWJwKWA9IHBlYWtfcG9zX01icCwgCiAgICBgTWVkaWF0b3IgbWlkcG9pbnQgKE1icClgID0gbWlkZGxlCiAgKQoKc2NhbjFfZmFjdG9yNF9kZiA8LSBzY2FuMV9mYWN0b3I0ICU+JSAKICBhcy5kYXRhLmZyYW1lKCApICU+JSAKICByZW5hbWUoIGBGYWN0b3IgNCBRVExgID0gIkZhY3RvcjQiKSAlPiUgCiAgbXV0YXRlKCBtYXJrZXIgPSBkaW1uYW1lcyhzY2FuMV9mYWN0b3I0KVtbMV1dKSAlPiUgCiAgbGVmdF9qb2luKG1hcF9kYXQyKSAKc2NhbjFfZmFjdG9yNF9kZiRjdW1zdW1fcG9zX2JwIDwtIHNjYW4xX2ZhY3RvcjRfZGYkcG9zX2JwICsgY2hyb21fbGVuc19vZmZzZXRbc2NhbjFfZmFjdG9yNF9kZiRjaHJdCnNjYW4xX2ZhY3RvcjRfZGYgPC0gc2NhbjFfZmFjdG9yNF9kZiAlPiUgCiAgbXV0YXRlKCBgT2ZmZXN0dGVkIGNocm9tb3NvbWUgY29vcmRpbmF0ZXNgID0gY3Vtc3VtX3Bvc19icCwKICAgICAgICAgIG1hcmtlciwKICAgICAgICAgIENocm9tb3NvbWUgPSBjaHIsCiAgICAgICAgICBgQ29vcmRpbmF0ZXMgKGNNKWAgPSBwb3NfY00sIAogICAgICAgICAgYENvb3JkaW5hdGVzIChicClgID0gcG9zX2JwKSAKCkZpZ3VyZTVFX2RhdGFfcXRsX3NjYW4gPC0gc2NhbjFfZmFjdG9yNF9kZiAlPiUgCiAgc2VsZWN0KCBgRmFjdG9yIDQgUVRMYCwgCiAgICAgICAgICBgT2ZmZXN0dGVkIGNocm9tb3NvbWUgY29vcmRpbmF0ZXNgID0gY3Vtc3VtX3Bvc19icCwKICAgICAgICAgIG1hcmtlciwKICAgICAgICAgIENocm9tb3NvbWUgPSBjaHIsCiAgICAgICAgICBgQ29vcmRpbmF0ZXMgKGNNKWAgPSBwb3NfY00sIAogICAgICAgICAgYENvb3JkaW5hdGVzIChicClgID0gcG9zX2JwKQoKRmlndXJlNUVfZGF0YV9tZWRpYXRpb24gPC0gZmFjdG9yNF9tZWRzICU+JSAKICBzZWxlY3QoCiAgICBgUGhlbm90eXBlYCA9IHBoZW5vdHlwZSwgCiAgICBgTWVkaWF0b3IgTUdJIHN5bWJvbGAgPSBtZ2lfc3ltYm9sLAogICAgYFFUTCBjaHJvbW9zb21lYCA9IHBlYWtfY2hyLCAKICAgIGBNZWRpYXRvciBjaHJvbW9zb21lYCA9IGNociwKICAgIGBRVEwgTE9EYCA9IHBlYWtfbG9kLAogICAgYE1lZGlhdGVkIExPRGAgPSBMT0QsCiAgICBgUVRMIHBvc2l0aW9uIChNYnApYD0gcGVha19wb3NfTWJwLCAKICAgIGBNZWRpYXRvciBtaWRwb2ludCAoTWJwKWAgPSBtaWRkbGUsCiAgICB0eXBlCiAgKQoKYGBgCgoKYGBge3IgRmlndXJlXzVFX3Bsb3QsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD00LCBmaWcuY2FwID0iRmlndXJlIDVFOkdlbm9tZS13aWRlIExPRCBzY29yZXMgb2J0YWluZWQgZnJvbSB0aGUgRmFjdG9yIDQgUVRMIHNjYW4gaXMgcGxvdHRlZCB3aXRoIG1lZGlhdGlvbiByZXN1bHRzIG92ZXJsYWlkLiBNZWRpYXRpb24gd2l0aCBHbTIwNjI1IHRyYW5zY3JpcHQgYWJ1bmRhbmNlIGNhdXNlcyB0aGUgbGFyZ2VzdCBkZWNyZWFzZSBpbiBRVEwgTE9EIHNjb3JlLiBEdXhmMyBleHByZXNzaW9uIHdhcyBwcmV2aW91c2x5IGlkZW50aWZpZWQgYXMgYSBzdHJvbmcgY2FuZGlkYXRlIG1lZGlhdG9yIGZvciB0aGUgZVFUTCBob3RzcG90IGluIHRoaXMgcmVnaW9uIChTa2VsbHkgZXQgYWwuLCAyMDIwKSwgYnV0IHBlcmZvcm1zIHBvb3JseSBhcyBhIG1lZGlhdG9yIG9mIHRoZSBGYWN0b3IgNCBRVEwgY29tcGFyZWQgdG8gR20yMDYyNS4gQm90aCBnZW5lcyBhcmUgaGlnaGxpZ2h0ZWQgaW4gZ3JlZW4gbmV4dCB0byB0aGVpciBjb3JyZXNwb25kaW5nIExPRCBzY29yZSBkcm9wLiIgfQoKCgojIGdyYXBoaWNhbCBwcmVwIGZvciBjaHJvbW9zb21lIGxvY2F0aW9ucwpjaHJvbXMgPC0gYyhhcy5jaGFyYWN0ZXIoMToxOSksICJYIikKY2hyb21fbGVucyA8LSBjKCAxOTU0MzE1NTksIDE4MjEwNzY3MCwgMTYwMDE3MTA0LCAxNTY0OTYwNzEsIDE1MTgzMzYyMCwgMTQ5NzIxODc0LCAxNDU0MzQ2OTMsIDEyOTM5OTQ2OCwgMTI0NTgyNjUwLCAxMzA2ODU0MTksIDEyMjA3ODY1MCwgMTIwMTIwNjIyICwxMjAzODcyNzIsIDEyNDg2NzcyNSwgMTA0MDE1NDUyLCA5ODE4MDAwMiwgOTQ5ODQ0MzIsIDkwNjcyNTk2LCA2MTQxNzMxMCAsIDE3MTAyODMwMCkKbmFtZXMoY2hyb21fbGVucykgPC0gY2hyb21zCmNocm9tX2xlbnNfb2Zmc2V0IDwtIGN1bXN1bShjaHJvbV9sZW5zKSAtIGNocm9tX2xlbnMKY2hyb21fbGVuc19taWRwdCA8LSBjaHJvbV9sZW5zX29mZnNldCArIGNocm9tX2xlbnMgLyAyCgojIGxvZCBwbG90IApGaWd1cmU1RV9kYXRhX3F0bF9zY2FuICU+JSAKICBnZ3Bsb3QoKSsKICAgIGFlcyggCiAgICAgIHg9IGBPZmZlc3R0ZWQgY2hyb21vc29tZSBjb29yZGluYXRlc2AsCiAgICAgIHkgPSBgRmFjdG9yIDQgUVRMYAogICAgICApKwogIGdlb21fbGluZSggc2l6ZSA9IDEsIGNvbCA9ICJkYXJrIGdyYXkiLGFscGhhID0gMC44KSsKICB0aGVtZV9wdWJjbGVhbiggYmFzZV9zaXplID0gMTgpKwogIHhsYWIoIkNocm9tb3NvbWUiKSsKICB5bGFiKCAiTE9EIHNjb3JlIikrCiAgc2NhbGVfeF9kaXNjcmV0ZSggbmFtZSA9ICJDaHJvbW9zb21lIiwKICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjaHJvbV9sZW5zX21pZHB0LCAKICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBuYW1lcyhjaHJvbV9sZW5zKSwgCiAgICAgICAgICAgICAgICAgICAgZXhwYW5kID0gZXhwYW5zaW9uKCBtdWx0ID0gMC4wNSkpIC0+IGZhY3RvcjRfbG9kX3Bsb3QKCiMgb3ZlcmxheSBtZWRpYXRpb24KIyBoaWdobGlnaHRzCmZhY3RvcjRfbWVkc19taW4gPC0gRmlndXJlNUVfZGF0YV9tZWRpYXRpb24gJT4lIAogIHNsaWNlX21pbiggYE1lZGlhdGVkIExPRGApCmZhY3RvcjRfbWVkc19kdXhmIDwtIEZpZ3VyZTVFX2RhdGFfbWVkaWF0aW9uICU+JSAKICBmaWx0ZXIoYE1lZGlhdG9yIE1HSSBzeW1ib2xgID09ICJEdXhmMyIpCgpmYWN0b3I0X2xvZF9wbG90KwogIGdlb21fcG9pbnQoIGRhdGEgPSBGaWd1cmU1RV9kYXRhX21lZGlhdGlvbiwgCiAgICAgICAgICAgICAgYWVzKCB4ID0gYE1lZGlhdG9yIG1pZHBvaW50IChNYnApYCoxZTA2K2Nocm9tX2xlbnNfb2Zmc2V0WzEwXSwKICAgICAgICAgICAgICAgICAgIHkgPSBgTWVkaWF0ZWQgTE9EYCksCiAgICAgICAgICAgICAgY29sID0gcXRsLmNvbG9yc1tbInJuYSJdXSwKICAgICAgICAgICAgICBzaXplID0gNCwKICAgICAgICAgICAgICBhbHBoYSA9IDAuOSkrCiAgYW5ub3RhdGUoICJsYWJlbCIsIAogICAgICAgICAgICB5ID0gZmFjdG9yNF9tZWRzX21pbiRgTWVkaWF0ZWQgTE9EYCswLjEsIAogICAgICAgICAgICB4ID0gZmFjdG9yNF9tZWRzX21pbiRgTWVkaWF0b3IgbWlkcG9pbnQgKE1icClgKjFlMDYrY2hyb21fbGVuc19vZmZzZXRbMTBdLTEuOWUwOCAsCiAgICAgICAgICAgIGxhYmVsID0gZmFjdG9yNF9tZWRzX21pbiRgTWVkaWF0b3IgTUdJIHN5bWJvbGAsIAogICAgICAgICAgICBzaXplID0gNiwgCiAgICAgICAgICAgIGNvbCA9IHF0bC5jb2xvcnNbWyJybmEiXV0sCiAgICAgICAgICAgIGZvbnRmYWNlID0gIml0YWxpYyIpKwogIGFubm90YXRlKCJsYWJlbCIsCiAgICAgICAgICAgeSA9IGZhY3RvcjRfbWVkc19kdXhmJGBNZWRpYXRlZCBMT0RgKzAuMSwgCiAgICAgICAgICAgIHggPWZhY3RvcjRfbWVkc19kdXhmJGBNZWRpYXRvciBtaWRwb2ludCAoTWJwKWAqMWUwNitjaHJvbV9sZW5zX29mZnNldFsxMF0tMS4zZTA4ICwKICAgICAgICAgICAgbGFiZWwgPSBmYWN0b3I0X21lZHNfZHV4ZiRgTWVkaWF0b3IgTUdJIHN5bWJvbGAsIAogICAgICAgICAgICBzaXplID0gNiwgCiAgICAgICAgICAgIGNvbCA9IHF0bC5jb2xvcnNbWyJybmEiXV0sCiAgICAgICAgICAgIGZvbnRmYWNlID0gIml0YWxpYyIpKwogIGdndGl0bGUoIk1PRkEgRmFjdG9yIDQgUVRMIiktPiBmYWN0b3I0X21lZF9wbG90CgpmYWN0b3I0X21lZF9wbG90CgpgYGAKCjxicj4KClFUTCBzY2FuIHVzZWQgaW4gcGxvdHRpbmcgRmlndXJlIDVFIGNhbiBiZSBkb3dubG9hZGVkIGJlbG93LgoKYGBge3IgRmlndXJlNWVfZGF0YSwgZmlnLmNhcCA9ICJRVEwgc2NhbiBwbG90dGVkIGluIEZpZ3VyZSA1RS4ifQoKbGlzdChGaWd1cmU1RV9kYXRhX3F0bF9zY2FuKSAlPiUgCiAgZG93bmxvYWR0aGlzOjpkb3dubG9hZF90aGlzKCAgICBvdXRwdXRfbmFtZSA9ICJGaWd1cmU1RSBkYXRhIiwKICAgIG91dHB1dF9leHRlbnNpb24gPSAiLnhsc3giLAogICAgYnV0dG9uX2xhYmVsID0gIkRvd25sb2FkIEZpZ3VyZSA1RSBkYXRhIGFzIHhsc3giLAogICAgYnV0dG9uX3R5cGUgPSAicHJpbWFyeSIsCiAgICBoYXNfaWNvbiA9IFRSVUUsCiAgICBpY29uID0gImZhIGZhLXNhdmUiCiAgICApCiAgCgpgYGAKCjxicj4KCmBgYHtyIEZpZ3VyZTVlX2RhdGEyLCBmaWcuY2FwID0gIk1lZGlhdGlvbiByZXN1bHRzIHBsb3R0ZWQgaW4gRmlndXJlIDVFLiJ9CgpGaWd1cmU1RV9kYXRhX21lZGlhdGlvbiAlPiUgCiAgbXV0YXRlX2lmKGlzLm51bWVyaWMsIHJvdW5kLCAyKSAlPiUgCiAgYXJyYW5nZShgTWVkaWF0ZWQgTE9EYCkgJT4lIAogIGNyZWF0ZV9kdCgpCgpgYGAKCjxicj4KPGJyPgoKCgoK

A work by Selcan Aydin

selcan.aydin@jax.org